diff --git a/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml b/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml new file mode 100644 index 00000000000..96f136abf0e --- /dev/null +++ b/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs b/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs new file mode 100644 index 00000000000..b0d0365ef6b --- /dev/null +++ b/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs @@ -0,0 +1,215 @@ +using Content.Client.Stylesheets; +using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.Monitor; +using Content.Shared.FixedPoint; +using Content.Shared.Temperature; +using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Map; +using System.Linq; + +namespace Content.Client.Atmos.Consoles; + +[GenerateTypedNameReferences] +public sealed partial class AtmosAlarmEntryContainer : BoxContainer +{ + public NetEntity NetEntity; + public EntityCoordinates? Coordinates; + + private readonly IEntityManager _entManager; + private readonly IResourceCache _cache; + + private Dictionary _alarmStrings = new Dictionary() + { + [AtmosAlarmType.Invalid] = "atmos-alerts-window-invalid-state", + [AtmosAlarmType.Normal] = "atmos-alerts-window-normal-state", + [AtmosAlarmType.Warning] = "atmos-alerts-window-warning-state", + [AtmosAlarmType.Danger] = "atmos-alerts-window-danger-state", + }; + + private Dictionary _gasShorthands = new Dictionary() + { + [Gas.Ammonia] = "NH₃", + [Gas.CarbonDioxide] = "CO₂", + [Gas.Frezon] = "F", + [Gas.Nitrogen] = "N₂", + [Gas.NitrousOxide] = "N₂O", + [Gas.Oxygen] = "O₂", + [Gas.Plasma] = "P", + [Gas.Tritium] = "T", + [Gas.WaterVapor] = "H₂O", + }; + + public AtmosAlarmEntryContainer(NetEntity uid, EntityCoordinates? coordinates) + { + RobustXamlLoader.Load(this); + + _entManager = IoCManager.Resolve(); + _cache = IoCManager.Resolve(); + + NetEntity = uid; + Coordinates = coordinates; + + // Load fonts + var headerFont = new VectorFont(_cache.GetResource("/Fonts/NotoSans/NotoSans-Bold.ttf"), 11); + var normalFont = new VectorFont(_cache.GetResource("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11); + var smallFont = new VectorFont(_cache.GetResource("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10); + + // Set fonts + TemperatureHeaderLabel.FontOverride = headerFont; + PressureHeaderLabel.FontOverride = headerFont; + OxygenationHeaderLabel.FontOverride = headerFont; + GasesHeaderLabel.FontOverride = headerFont; + + TemperatureLabel.FontOverride = normalFont; + PressureLabel.FontOverride = normalFont; + OxygenationLabel.FontOverride = normalFont; + + NoDataLabel.FontOverride = headerFont; + + SilenceCheckBox.Label.FontOverride = smallFont; + SilenceCheckBox.Label.FontColorOverride = Color.DarkGray; + } + + public void UpdateEntry(AtmosAlertsComputerEntry entry, bool isFocus, AtmosAlertsFocusDeviceData? focusData = null) + { + NetEntity = entry.NetEntity; + Coordinates = _entManager.GetCoordinates(entry.Coordinates); + + // Load fonts + var normalFont = new VectorFont(_cache.GetResource("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11); + + // Update alarm state + if (!_alarmStrings.TryGetValue(entry.AlarmState, out var alarmString)) + alarmString = "atmos-alerts-window-invalid-state"; + + AlarmStateLabel.Text = Loc.GetString(alarmString); + AlarmStateLabel.FontColorOverride = GetAlarmStateColor(entry.AlarmState); + + // Update alarm name + AlarmNameLabel.Text = Loc.GetString("atmos-alerts-window-alarm-label", ("name", entry.EntityName), ("address", entry.Address)); + + // Focus updates + FocusContainer.Visible = isFocus; + + if (isFocus) + SetAsFocus(); + else + RemoveAsFocus(); + + if (isFocus && entry.Group == AtmosAlertsComputerGroup.AirAlarm) + { + MainDataContainer.Visible = (entry.AlarmState != AtmosAlarmType.Invalid); + NoDataLabel.Visible = (entry.AlarmState == AtmosAlarmType.Invalid); + + if (focusData != null) + { + // Update temperature + var tempK = (FixedPoint2)focusData.Value.TemperatureData.Item1; + var tempC = (FixedPoint2)TemperatureHelpers.KelvinToCelsius(tempK.Float()); + + TemperatureLabel.Text = Loc.GetString("atmos-alerts-window-temperature-value", ("valueInC", tempC), ("valueInK", tempK)); + TemperatureLabel.FontColorOverride = GetAlarmStateColor(focusData.Value.TemperatureData.Item2); + + // Update pressure + PressureLabel.Text = Loc.GetString("atmos-alerts-window-pressure-value", ("value", (FixedPoint2)focusData.Value.PressureData.Item1)); + PressureLabel.FontColorOverride = GetAlarmStateColor(focusData.Value.PressureData.Item2); + + // Update oxygenation + var oxygenPercent = (FixedPoint2)0f; + var oxygenAlert = AtmosAlarmType.Invalid; + + if (focusData.Value.GasData.TryGetValue(Gas.Oxygen, out var oxygenData)) + { + oxygenPercent = oxygenData.Item2 * 100f; + oxygenAlert = oxygenData.Item3; + } + + OxygenationLabel.Text = Loc.GetString("atmos-alerts-window-oxygenation-value", ("value", oxygenPercent)); + OxygenationLabel.FontColorOverride = GetAlarmStateColor(oxygenAlert); + + // Update other present gases + GasGridContainer.RemoveAllChildren(); + + var gasData = focusData.Value.GasData.Where(g => g.Key != Gas.Oxygen); + + if (gasData.Count() == 0) + { + // No other gases + var gasLabel = new Label() + { + Text = Loc.GetString("atmos-alerts-window-other-gases-value-nil"), + FontOverride = normalFont, + FontColorOverride = StyleNano.DisabledFore, + HorizontalAlignment = HAlignment.Center, + VerticalAlignment = VAlignment.Center, + HorizontalExpand = true, + Margin = new Thickness(0, 2, 0, 0), + SetHeight = 24f, + }; + + GasGridContainer.AddChild(gasLabel); + } + + else + { + // Add an entry for each gas + foreach ((var gas, (var mol, var percent, var alert)) in gasData) + { + var gasPercent = (FixedPoint2)0f; + gasPercent = percent * 100f; + + if (!_gasShorthands.TryGetValue(gas, out var gasShorthand)) + gasShorthand = "X"; + + var gasLabel = new Label() + { + Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasShorthand), ("value", gasPercent)), + FontOverride = normalFont, + FontColorOverride = GetAlarmStateColor(alert), + HorizontalAlignment = HAlignment.Center, + VerticalAlignment = VAlignment.Center, + HorizontalExpand = true, + Margin = new Thickness(0, 2, 0, 0), + SetHeight = 24f, + }; + + GasGridContainer.AddChild(gasLabel); + } + } + } + } + } + + public void SetAsFocus() + { + FocusButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen); + ArrowTexture.TexturePath = "/Textures/Interface/Nano/inverted_triangle.svg.png"; + } + + public void RemoveAsFocus() + { + FocusButton.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen); + ArrowTexture.TexturePath = "/Textures/Interface/Nano/triangle_right.png"; + FocusContainer.Visible = false; + } + + private Color GetAlarmStateColor(AtmosAlarmType alarmType) + { + switch (alarmType) + { + case AtmosAlarmType.Normal: + return StyleNano.GoodGreenFore; + case AtmosAlarmType.Warning: + return StyleNano.ConcerningOrangeFore; + case AtmosAlarmType.Danger: + return StyleNano.DangerousRedFore; + } + + return StyleNano.DisabledFore; + } +} \ No newline at end of file diff --git a/Content.Client/Atmos/Consoles/AtmosAlertsComputerBoundUserInterface.cs b/Content.Client/Atmos/Consoles/AtmosAlertsComputerBoundUserInterface.cs new file mode 100644 index 00000000000..08cae979b9b --- /dev/null +++ b/Content.Client/Atmos/Consoles/AtmosAlertsComputerBoundUserInterface.cs @@ -0,0 +1,52 @@ +using Content.Shared.Atmos.Components; + +namespace Content.Client.Atmos.Consoles; + +public sealed class AtmosAlertsComputerBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private AtmosAlertsComputerWindow? _menu; + + public AtmosAlertsComputerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { } + + protected override void Open() + { + _menu = new AtmosAlertsComputerWindow(this, Owner); + _menu.OpenCentered(); + _menu.OnClose += Close; + + EntMan.TryGetComponent(Owner, out var xform); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + var castState = (AtmosAlertsComputerBoundInterfaceState) state; + + if (castState == null) + return; + + EntMan.TryGetComponent(Owner, out var xform); + _menu?.UpdateUI(xform?.Coordinates, castState.AirAlarms, castState.FireAlarms, castState.FocusData); + } + + public void SendFocusChangeMessage(NetEntity? netEntity) + { + SendMessage(new AtmosAlertsComputerFocusChangeMessage(netEntity)); + } + + public void SendDeviceSilencedMessage(NetEntity netEntity, bool silenceDevice) + { + SendMessage(new AtmosAlertsComputerDeviceSilencedMessage(netEntity, silenceDevice)); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + return; + + _menu?.Dispose(); + } +} diff --git a/Content.Client/Atmos/Consoles/AtmosAlertsComputerWindow.xaml b/Content.Client/Atmos/Consoles/AtmosAlertsComputerWindow.xaml new file mode 100644 index 00000000000..8824a776ee6 --- /dev/null +++ b/Content.Client/Atmos/Consoles/AtmosAlertsComputerWindow.xaml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content.Client/Atmos/Consoles/AtmosAlertsComputerWindow.xaml.cs b/Content.Client/Atmos/Consoles/AtmosAlertsComputerWindow.xaml.cs new file mode 100644 index 00000000000..a55321833cd --- /dev/null +++ b/Content.Client/Atmos/Consoles/AtmosAlertsComputerWindow.xaml.cs @@ -0,0 +1,550 @@ +using Content.Client.Message; +using Content.Client.Pinpointer.UI; +using Content.Client.Stylesheets; +using Content.Client.UserInterface.Controls; +using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.Monitor; +using Content.Shared.Pinpointer; +using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Map; +using Robust.Shared.Timing; +using Robust.Shared.Utility; +using Robust.Shared.ContentPack; +using Robust.Shared.Prototypes; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Content.Client.Atmos.Consoles; + +[GenerateTypedNameReferences] +public sealed partial class AtmosAlertsComputerWindow : FancyWindow +{ + private readonly IEntityManager _entManager; + private readonly SpriteSystem _spriteSystem; + + private EntityUid? _owner; + private NetEntity? _trackedEntity; + + private AtmosAlertsComputerEntry[]? _airAlarms = null; + private AtmosAlertsComputerEntry[]? _fireAlarms = null; + private IEnumerable? _allAlarms = null; + + private IEnumerable? _activeAlarms = null; + private Dictionary _deviceSilencingProgress = new(); + + public event Action? SendFocusChangeMessageAction; + public event Action? SendDeviceSilencedMessageAction; + + private bool _autoScrollActive = false; + private bool _autoScrollAwaitsUpdate = false; + + private const float SilencingDuration = 2.5f; + + public AtmosAlertsComputerWindow(AtmosAlertsComputerBoundUserInterface userInterface, EntityUid? owner) + { + RobustXamlLoader.Load(this); + _entManager = IoCManager.Resolve(); + _spriteSystem = _entManager.System(); + + // Pass the owner to nav map + _owner = owner; + NavMap.Owner = _owner; + + // Set nav map colors + NavMap.WallColor = new Color(64, 64, 64); + NavMap.TileColor = Color.DimGray * NavMap.WallColor; + + // Set nav map grid uid + var stationName = Loc.GetString("atmos-alerts-window-unknown-location"); + + if (_entManager.TryGetComponent(owner, out var xform)) + { + NavMap.MapUid = xform.GridUid; + + // Assign station name + if (_entManager.TryGetComponent(xform.GridUid, out var stationMetaData)) + stationName = stationMetaData.EntityName; + + var msg = new FormattedMessage(); + msg.AddMarkup(Loc.GetString("atmos-alerts-window-station-name", ("stationName", stationName))); + + StationName.SetMessage(msg); + } + + else + { + StationName.SetMessage(stationName); + NavMap.Visible = false; + } + + // Set trackable entity selected action + NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap; + + // Update nav map + NavMap.ForceNavMapUpdate(); + + // Set tab container headers + MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-alerts-window-tab-no-alerts")); + MasterTabContainer.SetTabTitle(1, Loc.GetString("atmos-alerts-window-tab-air-alarms")); + MasterTabContainer.SetTabTitle(2, Loc.GetString("atmos-alerts-window-tab-fire-alarms")); + + // Set UI toggles + ShowInactiveAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowInactiveAlarms, AtmosAlarmType.Invalid); + ShowNormalAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowNormalAlarms, AtmosAlarmType.Normal); + ShowWarningAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowWarningAlarms, AtmosAlarmType.Warning); + ShowDangerAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowDangerAlarms, AtmosAlarmType.Danger); + + // Set atmos monitoring message action + SendFocusChangeMessageAction += userInterface.SendFocusChangeMessage; + SendDeviceSilencedMessageAction += userInterface.SendDeviceSilencedMessage; + } + + #region Toggle handling + + private void OnShowAlarmsToggled(CheckBox toggle, AtmosAlarmType toggledAlarmState) + { + if (_owner == null) + return; + + if (!_entManager.TryGetComponent(_owner.Value, out var console)) + return; + + foreach (var device in console.AtmosDevices) + { + var alarmState = GetAlarmState(device.NetEntity); + + if (toggledAlarmState != alarmState) + continue; + + if (toggle.Pressed) + AddTrackedEntityToNavMap(device, alarmState); + + else + NavMap.TrackedEntities.Remove(device.NetEntity); + } + } + + private void OnSilenceAlertsToggled(NetEntity netEntity, bool toggleState) + { + if (!_entManager.TryGetComponent(_owner, out var console)) + return; + + if (toggleState) + _deviceSilencingProgress[netEntity] = SilencingDuration; + + else + _deviceSilencingProgress.Remove(netEntity); + + foreach (AtmosAlarmEntryContainer entryContainer in AlertsTable.Children) + { + if (entryContainer.NetEntity == netEntity) + entryContainer.SilenceAlarmProgressBar.Visible = toggleState; + } + + SendDeviceSilencedMessageAction?.Invoke(netEntity, toggleState); + } + + #endregion + + public void UpdateUI(EntityCoordinates? consoleCoords, AtmosAlertsComputerEntry[] airAlarms, AtmosAlertsComputerEntry[] fireAlarms, AtmosAlertsFocusDeviceData? focusData) + { + if (_owner == null) + return; + + if (!_entManager.TryGetComponent(_owner.Value, out var console)) + return; + + if (_trackedEntity != focusData?.NetEntity) + { + SendFocusChangeMessageAction?.Invoke(_trackedEntity); + focusData = null; + } + + // Retain alarm data for use inbetween updates + _airAlarms = airAlarms; + _fireAlarms = fireAlarms; + _allAlarms = airAlarms.Concat(fireAlarms); + + var silenced = console.SilencedDevices; + + _activeAlarms = _allAlarms.Where(x => x.AlarmState > AtmosAlarmType.Normal && + (!silenced.Contains(x.NetEntity) || _deviceSilencingProgress.ContainsKey(x.NetEntity))); + + // Reset nav map data + NavMap.TrackedCoordinates.Clear(); + NavMap.TrackedEntities.Clear(); + + // Add tracked entities to the nav map + foreach (var device in console.AtmosDevices) + { + if (!NavMap.Visible) + continue; + + var alarmState = GetAlarmState(device.NetEntity); + + if (_trackedEntity != device.NetEntity) + { + // Skip air alarms if the appropriate overlay is off + if (!ShowInactiveAlarms.Pressed && alarmState == AtmosAlarmType.Invalid) + continue; + + if (!ShowNormalAlarms.Pressed && alarmState == AtmosAlarmType.Normal) + continue; + + if (!ShowWarningAlarms.Pressed && alarmState == AtmosAlarmType.Warning) + continue; + + if (!ShowDangerAlarms.Pressed && alarmState == AtmosAlarmType.Danger) + continue; + } + + AddTrackedEntityToNavMap(device, alarmState); + } + + // Show the monitor location + var consoleUid = _entManager.GetNetEntity(_owner); + + if (consoleCoords != null && consoleUid != null) + { + var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png"))); + var blip = new NavMapBlip(consoleCoords.Value, texture, Color.Cyan, true, false); + NavMap.TrackedEntities[consoleUid.Value] = blip; + } + + // Update the nav map + NavMap.ForceNavMapUpdate(); + + // Clear excess children from the tables + var activeAlarmCount = _activeAlarms.Count(); + + while (AlertsTable.ChildCount > activeAlarmCount) + AlertsTable.RemoveChild(AlertsTable.GetChild(AlertsTable.ChildCount - 1)); + + while (AirAlarmsTable.ChildCount > airAlarms.Length) + AirAlarmsTable.RemoveChild(AirAlarmsTable.GetChild(AirAlarmsTable.ChildCount - 1)); + + while (FireAlarmsTable.ChildCount > fireAlarms.Length) + FireAlarmsTable.RemoveChild(FireAlarmsTable.GetChild(FireAlarmsTable.ChildCount - 1)); + + // Update all entries in each table + for (int index = 0; index < _activeAlarms.Count(); index++) + { + var entry = _activeAlarms.ElementAt(index); + UpdateUIEntry(entry, index, AlertsTable, console, focusData); + } + + for (int index = 0; index < airAlarms.Count(); index++) + { + var entry = airAlarms.ElementAt(index); + UpdateUIEntry(entry, index, AirAlarmsTable, console, focusData); + } + + for (int index = 0; index < fireAlarms.Count(); index++) + { + var entry = fireAlarms.ElementAt(index); + UpdateUIEntry(entry, index, FireAlarmsTable, console, focusData); + } + + // If no alerts are active, display a message + if (MasterTabContainer.CurrentTab == 0 && activeAlarmCount == 0) + { + var label = new RichTextLabel() + { + HorizontalExpand = true, + VerticalExpand = true, + HorizontalAlignment = HAlignment.Center, + VerticalAlignment = VAlignment.Center, + }; + + label.SetMarkup(Loc.GetString("atmos-alerts-window-no-active-alerts", ("color", StyleNano.GoodGreenFore.ToHexNoAlpha()))); + + AlertsTable.AddChild(label); + } + + // Update the alerts tab with the number of active alerts + if (activeAlarmCount == 0) + MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-alerts-window-tab-no-alerts")); + + else + MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-alerts-window-tab-alerts", ("value", activeAlarmCount))); + + // Auto-scroll re-enable + if (_autoScrollAwaitsUpdate) + { + _autoScrollActive = true; + _autoScrollAwaitsUpdate = false; + } + } + + private void AddTrackedEntityToNavMap(AtmosAlertsDeviceNavMapData metaData, AtmosAlarmType alarmState) + { + var data = GetBlipTexture(alarmState); + + if (data == null) + return; + + var texture = data.Value.Item1; + var color = data.Value.Item2; + var coords = _entManager.GetCoordinates(metaData.NetCoordinates); + + if (_trackedEntity != null && _trackedEntity != metaData.NetEntity) + color *= Color.DimGray; + + var selectable = true; + var blip = new NavMapBlip(coords, _spriteSystem.Frame0(texture), color, _trackedEntity == metaData.NetEntity, selectable); + + NavMap.TrackedEntities[metaData.NetEntity] = blip; + } + + private void UpdateUIEntry(AtmosAlertsComputerEntry entry, int index, Control table, AtmosAlertsComputerComponent console, AtmosAlertsFocusDeviceData? focusData = null) + { + // Make new UI entry if required + if (index >= table.ChildCount) + { + var newEntryContainer = new AtmosAlarmEntryContainer(entry.NetEntity, _entManager.GetCoordinates(entry.Coordinates)); + + // On click + newEntryContainer.FocusButton.OnButtonUp += args => + { + if (_trackedEntity == newEntryContainer.NetEntity) + { + _trackedEntity = null; + } + + else + { + _trackedEntity = newEntryContainer.NetEntity; + + if (newEntryContainer.Coordinates != null) + NavMap.CenterToCoordinates(newEntryContainer.Coordinates.Value); + } + + // Send message to console that the focus has changed + SendFocusChangeMessageAction?.Invoke(_trackedEntity); + + // Update affected UI elements across all tables + UpdateConsoleTable(console, AlertsTable, _trackedEntity); + UpdateConsoleTable(console, AirAlarmsTable, _trackedEntity); + UpdateConsoleTable(console, FireAlarmsTable, _trackedEntity); + }; + + // On toggling the silence check box + newEntryContainer.SilenceCheckBox.OnToggled += _ => OnSilenceAlertsToggled(newEntryContainer.NetEntity, newEntryContainer.SilenceCheckBox.Pressed); + + // Add the entry to the current table + table.AddChild(newEntryContainer); + } + + // Update values and UI elements + var tableChild = table.GetChild(index); + + if (tableChild is not AtmosAlarmEntryContainer) + { + table.RemoveChild(tableChild); + UpdateUIEntry(entry, index, table, console, focusData); + + return; + } + + var entryContainer = (AtmosAlarmEntryContainer)tableChild; + + entryContainer.UpdateEntry(entry, entry.NetEntity == _trackedEntity, focusData); + + if (_trackedEntity != entry.NetEntity) + { + var silenced = console.SilencedDevices; + entryContainer.SilenceCheckBox.Pressed = (silenced.Contains(entry.NetEntity) || _deviceSilencingProgress.ContainsKey(entry.NetEntity)); + } + + entryContainer.SilenceAlarmProgressBar.Visible = (table == AlertsTable && _deviceSilencingProgress.ContainsKey(entry.NetEntity)); + } + + private void UpdateConsoleTable(AtmosAlertsComputerComponent console, Control table, NetEntity? currTrackedEntity) + { + foreach (var tableChild in table.Children) + { + if (tableChild is not AtmosAlarmEntryContainer) + continue; + + var entryContainer = (AtmosAlarmEntryContainer)tableChild; + + if (entryContainer.NetEntity != currTrackedEntity) + entryContainer.RemoveAsFocus(); + + else if (entryContainer.NetEntity == currTrackedEntity) + entryContainer.SetAsFocus(); + } + } + + private void SetTrackedEntityFromNavMap(NetEntity? netEntity) + { + if (netEntity == null) + return; + + if (!_entManager.TryGetComponent(_owner, out var console)) + return; + + _trackedEntity = netEntity; + + if (netEntity != null) + { + // Tab switching + if (MasterTabContainer.CurrentTab != 0 || _activeAlarms?.Any(x => x.NetEntity == netEntity) == false) + { + var device = console.AtmosDevices.FirstOrNull(x => x.NetEntity == netEntity); + + switch (device?.Group) + { + case AtmosAlertsComputerGroup.AirAlarm: + MasterTabContainer.CurrentTab = 1; break; + case AtmosAlertsComputerGroup.FireAlarm: + MasterTabContainer.CurrentTab = 2; break; + } + } + + // Get the scroll position of the selected entity on the selected button the UI + ActivateAutoScrollToFocus(); + } + + // Send message to console that the focus has changed + SendFocusChangeMessageAction?.Invoke(_trackedEntity); + } + + protected override void FrameUpdate(FrameEventArgs args) + { + AutoScrollToFocus(); + + // Device silencing update + foreach ((var device, var remainingTime) in _deviceSilencingProgress) + { + var t = remainingTime - args.DeltaSeconds; + + if (t <= 0) + { + _deviceSilencingProgress.Remove(device); + + if (device == _trackedEntity) + _trackedEntity = null; + } + + else + _deviceSilencingProgress[device] = t; + } + } + + private void ActivateAutoScrollToFocus() + { + _autoScrollActive = false; + _autoScrollAwaitsUpdate = true; + } + + private void AutoScrollToFocus() + { + if (!_autoScrollActive) + return; + + var scroll = MasterTabContainer.Children.ElementAt(MasterTabContainer.CurrentTab) as ScrollContainer; + if (scroll == null) + return; + + if (!TryGetVerticalScrollbar(scroll, out var vScrollbar)) + return; + + if (!TryGetNextScrollPosition(out float? nextScrollPosition)) + return; + + vScrollbar.ValueTarget = nextScrollPosition.Value; + + if (MathHelper.CloseToPercent(vScrollbar.Value, vScrollbar.ValueTarget)) + _autoScrollActive = false; + } + + private bool TryGetVerticalScrollbar(ScrollContainer scroll, [NotNullWhen(true)] out VScrollBar? vScrollBar) + { + vScrollBar = null; + + foreach (var child in scroll.Children) + { + if (child is not VScrollBar) + continue; + + var castChild = child as VScrollBar; + + if (castChild != null) + { + vScrollBar = castChild; + return true; + } + } + + return false; + } + + private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition) + { + nextScrollPosition = null; + + var scroll = MasterTabContainer.Children.ElementAt(MasterTabContainer.CurrentTab) as ScrollContainer; + if (scroll == null) + return false; + + var container = scroll.Children.ElementAt(0) as BoxContainer; + if (container == null || container.Children.Count() == 0) + return false; + + // Exit if the heights of the children haven't been initialized yet + if (!container.Children.Any(x => x.Height > 0)) + return false; + + nextScrollPosition = 0; + + foreach (var control in container.Children) + { + if (control == null || control is not AtmosAlarmEntryContainer) + continue; + + if (((AtmosAlarmEntryContainer)control).NetEntity == _trackedEntity) + return true; + + nextScrollPosition += control.Height; + } + + // Failed to find control + nextScrollPosition = null; + + return false; + } + + private AtmosAlarmType GetAlarmState(NetEntity netEntity) + { + var alarmState = _allAlarms?.FirstOrNull(x => x.NetEntity == netEntity)?.AlarmState; + + if (alarmState == null) + return AtmosAlarmType.Invalid; + + return alarmState.Value; + } + + private (SpriteSpecifier.Texture, Color)? GetBlipTexture(AtmosAlarmType alarmState) + { + (SpriteSpecifier.Texture, Color)? output = null; + + switch (alarmState) + { + case AtmosAlarmType.Invalid: + output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")), StyleNano.DisabledFore); break; + case AtmosAlarmType.Normal: + output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")), Color.LimeGreen); break; + case AtmosAlarmType.Warning: + output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_triangle.png")), new Color(255, 182, 72)); break; + case AtmosAlarmType.Danger: + output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_square.png")), new Color(255, 67, 67)); break; + } + + return output; + } +} \ No newline at end of file diff --git a/Content.Client/Standing/LayingDownSystem.cs b/Content.Client/Standing/LayingDownSystem.cs index 594883ac001..3a1f438df05 100644 --- a/Content.Client/Standing/LayingDownSystem.cs +++ b/Content.Client/Standing/LayingDownSystem.cs @@ -4,6 +4,7 @@ using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Timing; +using DrawDepth = Content.Shared.DrawDepth.DrawDepth; namespace Content.Client.Standing; @@ -20,6 +21,8 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnMovementInput); + SubscribeNetworkEvent(OnDowned); + SubscribeLocalEvent(OnStood); SubscribeNetworkEvent(OnCheckAutoGetUp); } @@ -48,6 +51,29 @@ private void OnMovementInput(EntityUid uid, LayingDownComponent component, MoveE sprite.Rotation = Angle.FromDegrees(90); } + private void OnDowned(DrawDownedEvent args) + { + var uid = GetEntity(args.Uid); + + if (!TryComp(uid, out var sprite) + || !TryComp(uid, out var component)) + return; + + if (!component.OriginalDrawDepth.HasValue) + component.OriginalDrawDepth = sprite.DrawDepth; + + sprite.DrawDepth = (int) DrawDepth.SmallMobs; + } + + private void OnStood(EntityUid uid, LayingDownComponent component, StoodEvent args) + { + if (!TryComp(uid, out var sprite) + || !component.OriginalDrawDepth.HasValue) + return; + + sprite.DrawDepth = component.OriginalDrawDepth.Value; + } + private void OnCheckAutoGetUp(CheckAutoGetUpEvent ev, EntitySessionEventArgs args) { if (!_timing.IsFirstTimePredicted) diff --git a/Content.Client/Store/Ui/StoreMenu.xaml.cs b/Content.Client/Store/Ui/StoreMenu.xaml.cs index b7a2c285fe5..7eb597f2f39 100644 --- a/Content.Client/Store/Ui/StoreMenu.xaml.cs +++ b/Content.Client/Store/Ui/StoreMenu.xaml.cs @@ -3,6 +3,7 @@ using Content.Client.Message; using Content.Shared.FixedPoint; using Content.Shared.Store; +using Content.Client.Stylesheets; using Robust.Client.AutoGenerated; using Robust.Client.GameObjects; using Robust.Client.Graphics; @@ -147,6 +148,10 @@ private void AddListingGui(ListingData listing) } var newListing = new StoreListingControl(listing, GetListingPriceString(listing), hasBalance, texture); + + if (listing.DiscountValue > 0) + newListing.StoreItemBuyButton.AddStyleClass(StyleNano.ButtonColorDangerDefault.ToString()); + newListing.StoreItemBuyButton.OnButtonDown += args => OnListingButtonPressed?.Invoke(args, listing); diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs index f52f820a4ce..cd95a85f205 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs @@ -57,4 +57,3 @@ public async Task ChangeMachine() AssertPrototype("Autolathe"); } } - diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Constants.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Constants.cs index 11381fb8ccd..a915e5d47d1 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Constants.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Constants.cs @@ -1,3 +1,4 @@ + namespace Content.IntegrationTests.Tests.Interaction; // This partial class contains various constant prototype IDs common to interaction tests. @@ -27,8 +28,6 @@ public abstract partial class InteractionTest // Parts protected const string Bin1 = "MatterBinStockPart"; - protected const string Cap1 = "CapacitorStockPart"; protected const string Manipulator1 = "MicroManipulatorStockPart"; - protected const string Battery1 = "PowerCellSmall"; - protected const string Battery4 = "PowerCellHyper"; } + diff --git a/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs new file mode 100644 index 00000000000..85bae78dc6b --- /dev/null +++ b/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs @@ -0,0 +1,152 @@ +using Robust.Shared.Player; +using Content.Server.DoAfter; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Damage; +using Content.Shared.DoAfter; +using Content.Shared.Popups; +using Content.Shared.Psionics.Events; +using Content.Shared.Examine; +using static Content.Shared.Examine.ExamineSystemShared; +using Robust.Shared.Timing; +using Content.Shared.Actions.Events; +using Robust.Server.Audio; +using Content.Server.Atmos.Rotting; +using Content.Shared.Mobs.Systems; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Psionics.Glimmer; + +namespace Content.Server.Abilities.Psionics; + +public sealed class RevivifyPowerSystem : EntitySystem +{ + [Dependency] private readonly AudioSystem _audioSystem = default!; + [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly RottingSystem _rotting = default!; + [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly GlimmerSystem _glimmer = default!; + + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPowerUsed); + SubscribeLocalEvent(OnDispelled); + SubscribeLocalEvent(OnDoAfter); + } + + + private void OnPowerUsed(EntityUid uid, PsionicComponent component, PsionicHealOtherPowerActionEvent args) + { + if (component.DoAfter is not null) + return; + + if (!args.Immediate) + AttemptDoAfter(uid, component, args); + else ActivatePower(uid, component, args); + + if (args.PopupText is not null + && _glimmer.Glimmer > args.GlimmerObviousPopupThreshold * component.CurrentDampening) + _popupSystem.PopupEntity(Loc.GetString(args.PopupText, ("entity", uid)), uid, + Filter.Pvs(uid).RemoveWhereAttachedEntity(entity => !_examine.InRangeUnOccluded(uid, entity, ExamineRange, null)), + true, + args.PopupType); + + if (args.PlaySound + && _glimmer.Glimmer > args.GlimmerObviousSoundThreshold * component.CurrentDampening) + _audioSystem.PlayPvs(args.SoundUse, uid, args.AudioParams); + + // Sanitize the Glimmer inputs because otherwise the game will crash if someone makes MaxGlimmer lower than MinGlimmer. + var minGlimmer = (int) Math.Round(MathF.MinMagnitude(args.MinGlimmer, args.MaxGlimmer) + + component.CurrentAmplification - component.CurrentDampening); + var maxGlimmer = (int) Math.Round(MathF.MaxMagnitude(args.MinGlimmer, args.MaxGlimmer) + + component.CurrentAmplification - component.CurrentDampening); + + _psionics.LogPowerUsed(uid, args.PowerName, minGlimmer, maxGlimmer); + args.Handled = true; + } + + private void AttemptDoAfter(EntityUid uid, PsionicComponent component, PsionicHealOtherPowerActionEvent args) + { + var ev = new PsionicHealOtherDoAfterEvent(_gameTiming.CurTime); + ev.HealingAmount = args.HealingAmount; + ev.RotReduction = args.RotReduction; + ev.DoRevive = args.DoRevive; + var doAfterArgs = new DoAfterArgs(EntityManager, uid, args.UseDelay, ev, uid, target: args.Target) + { + BreakOnUserMove = args.BreakOnUserMove, + BreakOnTargetMove = args.BreakOnTargetMove, + }; + + if (!_doAfterSystem.TryStartDoAfter(doAfterArgs, out var doAfterId)) + return; + + component.DoAfter = doAfterId; + } + + private void OnDispelled(EntityUid uid, PsionicComponent component, DispelledEvent args) + { + if (component.DoAfter is null) + return; + + _doAfterSystem.Cancel(component.DoAfter); + component.DoAfter = null; + args.Handled = true; + } + + private void OnDoAfter(EntityUid uid, PsionicComponent component, PsionicHealOtherDoAfterEvent args) + { + // It's entirely possible for the caster to stop being Psionic(due to mindbreaking) mid cast + if (component is null) + return; + component.DoAfter = null; + + // The target can also cease existing mid-cast + if (args.Target is null) + return; + + _rotting.ReduceAccumulator(args.Target.Value, TimeSpan.FromSeconds(args.RotReduction * component.CurrentAmplification)); + + if (!TryComp(args.Target.Value, out var damageableComponent)) + return; + + _damageable.TryChangeDamage(args.Target.Value, args.HealingAmount * component.CurrentAmplification, true, false, damageableComponent, uid); + + if (!args.DoRevive + || !TryComp(args.Target, out var mob) + || !_mobThreshold.TryGetThresholdForState(args.Target.Value, MobState.Dead, out var threshold) + || damageableComponent.TotalDamage > threshold) + return; + + _mobState.ChangeMobState(args.Target.Value, MobState.Critical, mob, uid); + } + + // This would be the same thing as OnDoAfter, except that here the target isn't nullable, so I have to reuse code with different arguments. + private void ActivatePower(EntityUid uid, PsionicComponent component, PsionicHealOtherPowerActionEvent args) + { + if (component is null) + return; + + _rotting.ReduceAccumulator(args.Target, TimeSpan.FromSeconds(args.RotReduction * component.CurrentAmplification)); + + if (!TryComp(args.Target, out var damageableComponent)) + return; + + _damageable.TryChangeDamage(args.Target, args.HealingAmount * component.CurrentAmplification, true, false, damageableComponent, uid); + + if (!args.DoRevive + || !TryComp(args.Target, out var mob) + || !_mobThreshold.TryGetThresholdForState(args.Target, MobState.Dead, out var threshold) + || damageableComponent.TotalDamage > threshold) + return; + + _mobState.ChangeMobState(args.Target, MobState.Critical, mob, uid); + } +} diff --git a/Content.Server/Administration/Commands/SetOutfitCommand.cs b/Content.Server/Administration/Commands/SetOutfitCommand.cs index 12312286518..e19c5b72fa4 100644 --- a/Content.Server/Administration/Commands/SetOutfitCommand.cs +++ b/Content.Server/Administration/Commands/SetOutfitCommand.cs @@ -14,6 +14,7 @@ using Robust.Shared.Prototypes; using Content.Server.Silicon.IPC; using Content.Shared.Radio.Components; +using Content.Shared.Cluwne; namespace Content.Server.Administration.Commands { @@ -129,6 +130,8 @@ public static bool SetOutfit(EntityUid target, string gear, IEntityManager entit } } + if (entityManager.HasComponent(target)) + return true; //Fuck it, nuclear option for not Cluwning an IPC because that causes a crash that SOMEHOW ignores null checks. if (entityManager.HasComponent(target)) { var encryption = new InternalEncryptionKeySpawner(); diff --git a/Content.Server/Anomaly/AnomalySystem.Vessel.cs b/Content.Server/Anomaly/AnomalySystem.Vessel.cs index 35de5c1a646..9a6c99f8208 100644 --- a/Content.Server/Anomaly/AnomalySystem.Vessel.cs +++ b/Content.Server/Anomaly/AnomalySystem.Vessel.cs @@ -1,4 +1,5 @@ using Content.Server.Anomaly.Components; +using Content.Server.Construction; using Content.Server.Power.EntitySystems; using Content.Shared.Anomaly; using Content.Shared.Anomaly.Components; @@ -20,6 +21,7 @@ private void InitializeVessel() { SubscribeLocalEvent(OnVesselShutdown); SubscribeLocalEvent(OnVesselMapInit); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnVesselInteractUsing); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnVesselGetPointsPerSecond); @@ -65,6 +67,11 @@ private void OnVesselMapInit(EntityUid uid, AnomalyVesselComponent component, Ma UpdateVesselAppearance(uid, component); } + private void OnUpgradeExamine(EntityUid uid, AnomalyVesselComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("anomaly-vessel-component-upgrade-output", component.PointMultiplier); + } + private void OnVesselInteractUsing(EntityUid uid, AnomalyVesselComponent component, InteractUsingEvent args) { if (component.Anomaly != null || diff --git a/Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs b/Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs new file mode 100644 index 00000000000..758fde88f13 --- /dev/null +++ b/Content.Server/Atmos/Consoles/AtmosAlertsComputerSystem.cs @@ -0,0 +1,356 @@ +using Content.Server.Atmos.Monitor.Components; +using Content.Server.DeviceNetwork.Components; +using Content.Server.Power.Components; +using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.Consoles; +using Content.Shared.Atmos.Monitor; +using Content.Shared.Atmos.Monitor.Components; +using Content.Shared.Pinpointer; +using Robust.Server.GameObjects; +using Robust.Shared.Map.Components; +using Robust.Shared.Player; +using Robust.Shared.ContentPack; +using Robust.Shared.Prototypes; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Shared.Access.Components; +using Content.Shared.Database; +using Content.Shared.NameIdentifier; +using Content.Shared.Stacks; +using JetBrains.Annotations; +using Robust.Shared.Utility; + +namespace Content.Server.Atmos.Monitor.Systems; + +public sealed class AtmosAlertsComputerSystem : SharedAtmosAlertsComputerSystem +{ + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly AirAlarmSystem _airAlarmSystem = default!; + [Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + + private const float UpdateTime = 1.0f; + + // Note: this data does not need to be saved + private float _updateTimer = 1.0f; + + public override void Initialize() + { + base.Initialize(); + + // Console events + SubscribeLocalEvent(OnConsoleInit); + SubscribeLocalEvent(OnConsoleParentChanged); + SubscribeLocalEvent(OnFocusChangedMessage); + + // Grid events + SubscribeLocalEvent(OnGridSplit); + SubscribeLocalEvent(OnDeviceAnchorChanged); + } + + #region Event handling + + private void OnConsoleInit(EntityUid uid, AtmosAlertsComputerComponent component, ComponentInit args) + { + InitalizeConsole(uid, component); + } + + private void OnConsoleParentChanged(EntityUid uid, AtmosAlertsComputerComponent component, EntParentChangedMessage args) + { + InitalizeConsole(uid, component); + } + + private void OnFocusChangedMessage(EntityUid uid, AtmosAlertsComputerComponent component, AtmosAlertsComputerFocusChangeMessage args) + { + component.FocusDevice = args.FocusDevice; + } + + private void OnGridSplit(ref GridSplitEvent args) + { + // Collect grids + var allGrids = args.NewGrids.ToList(); + + if (!allGrids.Contains(args.Grid)) + allGrids.Add(args.Grid); + + // Update atmos monitoring consoles that stand upon an updated grid + var query = AllEntityQuery(); + while (query.MoveNext(out var ent, out var entConsole, out var entXform)) + { + if (entXform.GridUid == null) + continue; + + if (!allGrids.Contains(entXform.GridUid.Value)) + continue; + + InitalizeConsole(ent, entConsole); + } + } + + private void OnDeviceAnchorChanged(EntityUid uid, AtmosAlertsDeviceComponent component, AnchorStateChangedEvent args) + { + var xform = Transform(uid); + var gridUid = xform.GridUid; + + if (gridUid == null) + return; + + if (!TryGetAtmosDeviceNavMapData(uid, component, xform, gridUid.Value, out var data)) + return; + + var netEntity = EntityManager.GetNetEntity(uid); + + var query = AllEntityQuery(); + while (query.MoveNext(out var ent, out var entConsole, out var entXform)) + { + if (gridUid != entXform.GridUid) + continue; + + if (args.Anchored) + entConsole.AtmosDevices.Add(data.Value); + + else if (!args.Anchored) + entConsole.AtmosDevices.RemoveWhere(x => x.NetEntity == netEntity); + } + } + + #endregion + + public override void Update(float frameTime) + { + base.Update(frameTime); + + _updateTimer += frameTime; + + if (_updateTimer >= UpdateTime) + { + _updateTimer -= UpdateTime; + + // Keep a list of UI entries for each gridUid, in case multiple consoles stand on the same grid + var airAlarmEntriesForEachGrid = new Dictionary(); + var fireAlarmEntriesForEachGrid = new Dictionary(); + + var query = AllEntityQuery(); + while (query.MoveNext(out var ent, out var entConsole, out var entXform)) + { + if (entXform?.GridUid == null) + continue; + + // Make a list of alarm state data for all the air and fire alarms on the grid + if (!airAlarmEntriesForEachGrid.TryGetValue(entXform.GridUid.Value, out var airAlarmEntries)) + { + airAlarmEntries = GetAlarmStateData(entXform.GridUid.Value, AtmosAlertsComputerGroup.AirAlarm).ToArray(); + airAlarmEntriesForEachGrid[entXform.GridUid.Value] = airAlarmEntries; + } + + if (!fireAlarmEntriesForEachGrid.TryGetValue(entXform.GridUid.Value, out var fireAlarmEntries)) + { + fireAlarmEntries = GetAlarmStateData(entXform.GridUid.Value, AtmosAlertsComputerGroup.FireAlarm).ToArray(); + fireAlarmEntriesForEachGrid[entXform.GridUid.Value] = fireAlarmEntries; + } + + // Determine the highest level of alert for the console (based on non-silenced alarms) + var highestAlert = AtmosAlarmType.Invalid; + + foreach (var entry in airAlarmEntries) + { + if (entry.AlarmState > highestAlert && !entConsole.SilencedDevices.Contains(entry.NetEntity)) + highestAlert = entry.AlarmState; + } + + foreach (var entry in fireAlarmEntries) + { + if (entry.AlarmState > highestAlert && !entConsole.SilencedDevices.Contains(entry.NetEntity)) + highestAlert = entry.AlarmState; + } + + // Update the appearance of the console based on the highest recorded level of alert + if (TryComp(ent, out var entAppearance)) + _appearance.SetData(ent, AtmosAlertsComputerVisuals.ComputerLayerScreen, (int) highestAlert, entAppearance); + + // If the console UI is open, send UI data to each subscribed session + UpdateUIState(ent, airAlarmEntries, fireAlarmEntries, entConsole, entXform); + } + } + } + + public void UpdateUIState + (EntityUid uid, + AtmosAlertsComputerEntry[] airAlarmStateData, + AtmosAlertsComputerEntry[] fireAlarmStateData, + AtmosAlertsComputerComponent component, + TransformComponent xform) + { + if (!_uiSystem.IsUiOpen(uid, AtmosAlertsComputerUiKey.Key)) + return; + + var gridUid = xform.GridUid!.Value; + + if (!HasComp(gridUid)) + return; + + // The grid must have a NavMapComponent to visualize the map in the UI + EnsureComp(gridUid); + + // Gathering remaining data to be send to the client + var focusAlarmData = GetFocusAlarmData(uid, GetEntity(component.FocusDevice), gridUid); + + // Set the UI state + _uiSystem.TrySetUiState(uid, AtmosAlertsComputerUiKey.Key, + new AtmosAlertsComputerBoundInterfaceState(airAlarmStateData, fireAlarmStateData, focusAlarmData)); + } + + private List GetAlarmStateData(EntityUid gridUid, AtmosAlertsComputerGroup group) + { + var alarmStateData = new List(); + + var queryAlarms = AllEntityQuery(); + while (queryAlarms.MoveNext(out var ent, out var entDevice, out var entAtmosAlarmable, out var entDeviceNetwork, out var entXform)) + { + if (entXform.GridUid != gridUid) + continue; + + if (!entXform.Anchored) + continue; + + if (entDevice.Group != group) + continue; + + // If emagged, change the alarm type to normal + var alarmState = (entAtmosAlarmable.LastAlarmState == AtmosAlarmType.Emagged) ? AtmosAlarmType.Normal : entAtmosAlarmable.LastAlarmState; + + // Unpowered alarms can't sound + if (TryComp(ent, out var entAPCPower) && !entAPCPower.Powered) + alarmState = AtmosAlarmType.Invalid; + + var entry = new AtmosAlertsComputerEntry + (GetNetEntity(ent), + GetNetCoordinates(entXform.Coordinates), + entDevice.Group, + alarmState, + MetaData(ent).EntityName, + entDeviceNetwork.Address); + + alarmStateData.Add(entry); + } + + return alarmStateData; + } + + private AtmosAlertsFocusDeviceData? GetFocusAlarmData(EntityUid uid, EntityUid? focusDevice, EntityUid gridUid) + { + if (focusDevice == null) + return null; + + var focusDeviceXform = Transform(focusDevice.Value); + + if (!focusDeviceXform.Anchored || + focusDeviceXform.GridUid != gridUid || + !TryComp(focusDevice.Value, out var focusDeviceAirAlarm)) + { + return null; + } + + // Force update the sensors attached to the alarm + if (!_uiSystem.IsUiOpen(focusDevice.Value, SharedAirAlarmInterfaceKey.Key)) + { + _atmosDevNet.Register(focusDevice.Value, null); + _atmosDevNet.Sync(focusDevice.Value, null); + + foreach ((var address, var _) in focusDeviceAirAlarm.SensorData) + _atmosDevNet.Register(uid, null); + } + + // Get the sensor data + var temperatureData = (_airAlarmSystem.CalculateTemperatureAverage(focusDeviceAirAlarm), AtmosAlarmType.Normal); + var pressureData = (_airAlarmSystem.CalculatePressureAverage(focusDeviceAirAlarm), AtmosAlarmType.Normal); + var gasData = new Dictionary(); + + foreach ((var address, var sensorData) in focusDeviceAirAlarm.SensorData) + { + if (sensorData.TemperatureThreshold.CheckThreshold(sensorData.Temperature, out var temperatureState) && + (int) temperatureState > (int) temperatureData.Item2) + { + temperatureData = (temperatureData.Item1, temperatureState); + } + + if (sensorData.PressureThreshold.CheckThreshold(sensorData.Pressure, out var pressureState) && + (int) pressureState > (int) pressureData.Item2) + { + pressureData = (pressureData.Item1, pressureState); + } + + if (focusDeviceAirAlarm.SensorData.Sum(g => g.Value.TotalMoles) > 1e-8) + { + foreach ((var gas, var threshold) in sensorData.GasThresholds) + { + if (!gasData.ContainsKey(gas)) + { + float mol = _airAlarmSystem.CalculateGasMolarConcentrationAverage(focusDeviceAirAlarm, gas, out var percentage); + + if (mol < 1e-8) + continue; + + gasData[gas] = (mol, percentage, AtmosAlarmType.Normal); + } + + if (threshold.CheckThreshold(gasData[gas].Item2, out var gasState) && + (int) gasState > (int) gasData[gas].Item3) + { + gasData[gas] = (gasData[gas].Item1, gasData[gas].Item2, gasState); + } + } + } + } + + return new AtmosAlertsFocusDeviceData(GetNetEntity(focusDevice.Value), temperatureData, pressureData, gasData); + } + + private HashSet GetAllAtmosDeviceNavMapData(EntityUid gridUid) + { + var atmosDeviceNavMapData = new HashSet(); + + var query = AllEntityQuery(); + while (query.MoveNext(out var ent, out var entComponent, out var entXform)) + { + if (TryGetAtmosDeviceNavMapData(ent, entComponent, entXform, gridUid, out var data)) + atmosDeviceNavMapData.Add(data.Value); + } + + return atmosDeviceNavMapData; + } + + private bool TryGetAtmosDeviceNavMapData + (EntityUid uid, + AtmosAlertsDeviceComponent component, + TransformComponent xform, + EntityUid gridUid, + [NotNullWhen(true)] out AtmosAlertsDeviceNavMapData? output) + { + output = null; + + if (xform.GridUid != gridUid) + return false; + + if (!xform.Anchored) + return false; + + output = new AtmosAlertsDeviceNavMapData(GetNetEntity(uid), GetNetCoordinates(xform.Coordinates), component.Group); + + return true; + } + + private void InitalizeConsole(EntityUid uid, AtmosAlertsComputerComponent component) + { + var xform = Transform(uid); + + if (xform.GridUid == null) + return; + + var grid = xform.GridUid.Value; + component.AtmosDevices = GetAllAtmosDeviceNavMapData(grid); + + Dirty(uid, component); + } +} diff --git a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs index 2922d0796a9..240f21ad42e 100644 --- a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs @@ -593,6 +593,21 @@ public float CalculateTemperatureAverage(AirAlarmComponent alarm) : 0f; } + public float CalculateGasMolarConcentrationAverage(AirAlarmComponent alarm, Gas gas, out float percentage) + { + percentage = 0f; + + var data = alarm.SensorData.Values.SelectMany(v => v.Gases.Where(g => g.Key == gas)); + + if (data.Count() == 0) + return 0f; + + var averageMol = data.Select(kvp => kvp.Value).Average(); + percentage = data.Select(kvp => kvp.Value).Sum() / alarm.SensorData.Values.Select(v => v.TotalMoles).Sum(); + + return averageMol; + } + public void UpdateUI(EntityUid uid, AirAlarmComponent? alarm = null, DeviceNetworkComponent? devNet = null, AtmosAlarmableComponent? alarmable = null) { if (!Resolve(uid, ref alarm, ref devNet, ref alarmable)) diff --git a/Content.Server/Atmos/Piping/Binary/Components/GasRecyclerComponent.cs b/Content.Server/Atmos/Piping/Binary/Components/GasRecyclerComponent.cs index e1eb0072b9d..aa7e3e0b360 100644 --- a/Content.Server/Atmos/Piping/Binary/Components/GasRecyclerComponent.cs +++ b/Content.Server/Atmos/Piping/Binary/Components/GasRecyclerComponent.cs @@ -1,11 +1,12 @@ using Content.Shared.Atmos; +using Content.Shared.Construction.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Atmos.Piping.Binary.Components { [RegisterComponent] public sealed partial class GasRecyclerComponent : Component { - [ViewVariables(VVAccess.ReadOnly)] [DataField("reacting")] public Boolean Reacting { get; set; } = false; @@ -17,10 +18,28 @@ public sealed partial class GasRecyclerComponent : Component [DataField("outlet")] public string OutletName { get; set; } = "outlet"; - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float MinTemp = 300 + Atmospherics.T0C; - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] + public float BaseMinTemp = 300 + Atmospherics.T0C; + + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartMinTemp = "Capacitor"; + + [DataField] + public float PartRatingMinTempMultiplier = 0.95f; + + [ViewVariables(VVAccess.ReadWrite)] public float MinPressure = 30 * Atmospherics.OneAtmosphere; + + [DataField] + public float BaseMinPressure = 30 * Atmospherics.OneAtmosphere; + + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartMinPressure = "Manipulator"; + + [DataField] + public float PartRatingMinPressureMultiplier = 0.8f; } } diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs index 3ebc5094926..40b9d88846f 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Piping.Binary.Components; using Content.Server.Atmos.Piping.Components; +using Content.Server.Construction; using Content.Server.NodeContainer; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.Nodes; @@ -28,6 +29,8 @@ public override void Initialize() SubscribeLocalEvent(OnUpdate); SubscribeLocalEvent(OnDisabled); SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); } private void OnEnabled(EntityUid uid, GasRecyclerComponent comp, ref AtmosDeviceEnabledEvent args) @@ -116,5 +119,20 @@ private void UpdateAppearance(EntityUid uid, GasRecyclerComponent? comp = null) _appearance.SetData(uid, PumpVisuals.Enabled, comp.Reacting); } + + private void OnRefreshParts(EntityUid uid, GasRecyclerComponent component, RefreshPartsEvent args) + { + var ratingTemp = args.PartRatings[component.MachinePartMinTemp]; + var ratingPressure = args.PartRatings[component.MachinePartMinPressure]; + + component.MinTemp = component.BaseMinTemp * MathF.Pow(component.PartRatingMinTempMultiplier, ratingTemp - 1); + component.MinPressure = component.BaseMinPressure * MathF.Pow(component.PartRatingMinPressureMultiplier, ratingPressure - 1); + } + + private void OnUpgradeExamine(EntityUid uid, GasRecyclerComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("gas-recycler-upgrade-min-temp", component.MinTemp / component.BaseMinTemp); + args.AddPercentageUpgrade("gas-recycler-upgrade-min-pressure", component.MinPressure / component.BaseMinPressure); + } } } diff --git a/Content.Server/Atmos/Portable/PortableScrubberComponent.cs b/Content.Server/Atmos/Portable/PortableScrubberComponent.cs index ae9a5da9639..fbe2d3f95a0 100644 --- a/Content.Server/Atmos/Portable/PortableScrubberComponent.cs +++ b/Content.Server/Atmos/Portable/PortableScrubberComponent.cs @@ -1,4 +1,6 @@ using Content.Shared.Atmos; +using Content.Shared.Construction.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Atmos.Portable { @@ -37,13 +39,51 @@ public sealed partial class PortableScrubberComponent : Component /// /// Maximum internal pressure before it refuses to take more. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float MaxPressure = 2500; /// - /// The speed at which gas is scrubbed from the environment. + /// The base amount of maximum internal pressure /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] + public float BaseMaxPressure = 2500; + + /// + /// The machine part that modifies the maximum internal pressure + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartMaxPressure = "MatterBin"; + + /// + /// How much the will affect the pressure. + /// The value will be multiplied by this amount for each increasing part tier. + /// + [DataField] + public float PartRatingMaxPressureModifier = 1.5f; + + /// + /// The speed at which gas is scrubbed from the environment. + /// + [ViewVariables(VVAccess.ReadWrite)] public float TransferRate = 800; + + /// + /// The base speed at which gas is scrubbed from the environment. + /// + [DataField] + public float BaseTransferRate = 800; + + /// + /// The machine part which modifies the speed of + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartTransferRate = "Manipulator"; + + /// + /// How much the will modify the rate. + /// The value will be multiplied by this amount for each increasing part tier. + /// + [DataField] + public float PartRatingTransferRateModifier = 1.4f; } } diff --git a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs index f9043c091a8..f657d713d28 100644 --- a/Content.Server/Atmos/Portable/PortableScrubberSystem.cs +++ b/Content.Server/Atmos/Portable/PortableScrubberSystem.cs @@ -12,6 +12,7 @@ using Content.Server.NodeContainer.NodeGroups; using Content.Server.Audio; using Content.Server.Administration.Logs; +using Content.Server.Construction; using Content.Server.NodeContainer.EntitySystems; using Content.Shared.Database; @@ -38,6 +39,8 @@ public override void Initialize() SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnDestroyed); SubscribeLocalEvent(OnScrubberAnalyzed); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); } private bool IsFull(PortableScrubberComponent component) @@ -156,5 +159,20 @@ private void OnScrubberAnalyzed(EntityUid uid, PortableScrubberComponent compone if (_nodeContainer.TryGetNode(uid, component.PortName, out PipeNode? port)) args.GasMixtures.Add(component.PortName, port.Air); } + + private void OnRefreshParts(EntityUid uid, PortableScrubberComponent component, RefreshPartsEvent args) + { + var pressureRating = args.PartRatings[component.MachinePartMaxPressure]; + var transferRating = args.PartRatings[component.MachinePartTransferRate]; + + component.MaxPressure = component.BaseMaxPressure * MathF.Pow(component.PartRatingMaxPressureModifier, pressureRating - 1); + component.TransferRate = component.BaseTransferRate * MathF.Pow(component.PartRatingTransferRateModifier, transferRating - 1); + } + + private void OnUpgradeExamine(EntityUid uid, PortableScrubberComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("portable-scrubber-component-upgrade-max-pressure", component.MaxPressure / component.BaseMaxPressure); + args.AddPercentageUpgrade("portable-scrubber-component-upgrade-transfer-rate", component.TransferRate / component.BaseTransferRate); + } } } diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs index f1bd9482e5e..976ef5139c3 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Bed.Components; using Content.Server.Bed.Sleep; using Content.Server.Body.Systems; +using Content.Server.Construction; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Bed; @@ -9,6 +10,7 @@ using Content.Shared.Body.Components; using Content.Shared.Buckle.Components; using Content.Shared.Damage; +using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; using Content.Shared.Mobs.Systems; using Robust.Shared.Timing; @@ -32,6 +34,8 @@ public override void Initialize() SubscribeLocalEvent(OnBuckleChange); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnEmagged); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); } private void ManageUpdateList(EntityUid uid, HealOnBuckleComponent component, ref BuckleChangeEvent args) @@ -66,7 +70,7 @@ public override void Update(float frameTime) foreach (var healedEntity in strapComponent.BuckledEntities) { - if (_mobStateSystem.IsDead(healedEntity) + if (_mobStateSystem.IsDead(healedEntity) || HasComp(healedEntity)) continue; @@ -126,5 +130,18 @@ private void UpdateMetabolisms(EntityUid uid, StasisBedComponent component, bool RaiseLocalEvent(buckledEntity, ref metabolicEvent); } } + + private void OnRefreshParts(EntityUid uid, StasisBedComponent component, RefreshPartsEvent args) + { + var metabolismRating = args.PartRatings[component.MachinePartMetabolismModifier]; + component.Multiplier = component.BaseMultiplier * metabolismRating; // Linear scaling so it's not OP + if (HasComp(uid)) + component.Multiplier = 1f / component.Multiplier; + } + + private void OnUpgradeExamine(EntityUid uid, StasisBedComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("stasis-bed-component-upgrade-stasis", component.Multiplier / component.BaseMultiplier); + } } } diff --git a/Content.Server/Bed/Components/StasisBedComponent.cs b/Content.Server/Bed/Components/StasisBedComponent.cs index e2175d6e646..bb4096a2a5e 100644 --- a/Content.Server/Bed/Components/StasisBedComponent.cs +++ b/Content.Server/Bed/Components/StasisBedComponent.cs @@ -1,12 +1,21 @@ +using Content.Shared.Construction.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + namespace Content.Server.Bed.Components { [RegisterComponent] public sealed partial class StasisBedComponent : Component { + [DataField] + public float BaseMultiplier = 10f; + /// - /// What the metabolic update rate will be multiplied by (higher = slower metabolism) + /// What the metabolic update rate will be multiplied by (higher = slower metabolism) /// [ViewVariables(VVAccess.ReadWrite)] public float Multiplier = 10f; + + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartMetabolismModifier = "Capacitor"; } } diff --git a/Content.Server/Botany/Components/SeedExtractorComponent.cs b/Content.Server/Botany/Components/SeedExtractorComponent.cs index ddb04f213d1..d765e079cec 100644 --- a/Content.Server/Botany/Components/SeedExtractorComponent.cs +++ b/Content.Server/Botany/Components/SeedExtractorComponent.cs @@ -1,4 +1,7 @@ using Content.Server.Botany.Systems; +using Content.Server.Construction; +using Content.Shared.Construction.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Botany.Components; @@ -7,14 +10,33 @@ namespace Content.Server.Botany.Components; public sealed partial class SeedExtractorComponent : Component { /// - /// The minimum amount of seed packets dropped. + /// The minimum amount of seed packets dropped with no machine upgrades. /// - [DataField("baseMinSeeds"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public int BaseMinSeeds = 1; /// - /// The maximum amount of seed packets dropped. + /// The maximum amount of seed packets dropped with no machine upgrades. /// - [DataField("baseMaxSeeds"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public int BaseMaxSeeds = 3; + + /// + /// Modifier to the amount of seeds outputted, set on . + /// + [ViewVariables(VVAccess.ReadWrite)] + public float SeedAmountMultiplier; + + /// + /// Machine part whose rating modifies the amount of seed packets dropped. + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartSeedAmount = "Manipulator"; + + /// + /// How much the machine part quality affects the amount of seeds outputted. + /// Going up a tier will multiply the seed output by this amount. + /// + [DataField] + public float PartRatingSeedAmountMultiplier = 1.5f; } diff --git a/Content.Server/Botany/Systems/SeedExtractorSystem.cs b/Content.Server/Botany/Systems/SeedExtractorSystem.cs index f1ae6c9f11a..4c547b96f09 100644 --- a/Content.Server/Botany/Systems/SeedExtractorSystem.cs +++ b/Content.Server/Botany/Systems/SeedExtractorSystem.cs @@ -1,8 +1,10 @@ using Content.Server.Botany.Components; +using Content.Server.Construction; using Content.Server.Popups; using Content.Server.Power.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Popups; +using Robust.Shared.Player; using Robust.Shared.Random; namespace Content.Server.Botany.Systems; @@ -18,6 +20,8 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); } private void OnInteractUsing(EntityUid uid, SeedExtractorComponent seedExtractor, InteractUsingEvent args) @@ -25,8 +29,7 @@ private void OnInteractUsing(EntityUid uid, SeedExtractorComponent seedExtractor if (!this.IsPowered(uid, EntityManager)) return; - if (!TryComp(args.Used, out ProduceComponent? produce)) - return; + if (!TryComp(args.Used, out ProduceComponent? produce)) return; if (!_botanySystem.TryGetSeed(produce, out var seed) || seed.Seedless) { _popupSystem.PopupCursor(Loc.GetString("seed-extractor-component-no-seeds",("name", args.Used)), @@ -39,7 +42,7 @@ private void OnInteractUsing(EntityUid uid, SeedExtractorComponent seedExtractor QueueDel(args.Used); - var amount = _random.Next(seedExtractor.BaseMinSeeds, seedExtractor.BaseMaxSeeds + 1); + var amount = (int) _random.NextFloat(seedExtractor.BaseMinSeeds, seedExtractor.BaseMaxSeeds + 1) * seedExtractor.SeedAmountMultiplier; var coords = Transform(uid).Coordinates; if (amount > 1) @@ -50,4 +53,15 @@ private void OnInteractUsing(EntityUid uid, SeedExtractorComponent seedExtractor _botanySystem.SpawnSeedPacket(seed, coords, args.User); } } + + private void OnRefreshParts(EntityUid uid, SeedExtractorComponent seedExtractor, RefreshPartsEvent args) + { + var manipulatorQuality = args.PartRatings[seedExtractor.MachinePartSeedAmount]; + seedExtractor.SeedAmountMultiplier = MathF.Pow(seedExtractor.PartRatingSeedAmountMultiplier, manipulatorQuality - 1); + } + + private void OnUpgradeExamine(EntityUid uid, SeedExtractorComponent seedExtractor, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("seed-extractor-component-upgrade-seed-yield", seedExtractor.SeedAmountMultiplier); + } } diff --git a/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs b/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs index 42aabf2578e..1c25b0e79d2 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Telepad.cs @@ -1,4 +1,6 @@ using Content.Server.Cargo.Components; +using Content.Server.Construction; +using Content.Server.Paper; using Content.Server.Power.Components; using Content.Shared.Cargo; using Content.Shared.Cargo.Components; @@ -13,6 +15,8 @@ public sealed partial class CargoSystem private void InitializeTelepad() { SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnTelepadPowerChange); // Shouldn't need re-anchored event SubscribeLocalEvent(OnTelepadAnchorChange); @@ -79,6 +83,17 @@ private void OnInit(EntityUid uid, CargoTelepadComponent telepad, ComponentInit _linker.EnsureSinkPorts(uid, telepad.ReceiverPort); } + private void OnRefreshParts(EntityUid uid, CargoTelepadComponent component, RefreshPartsEvent args) + { + var rating = args.PartRatings[component.MachinePartTeleportDelay] - 1; + component.Delay = component.BaseDelay * MathF.Pow(component.PartRatingTeleportDelay, rating); + } + + private void OnUpgradeExamine(EntityUid uid, CargoTelepadComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("cargo-telepad-delay-upgrade", component.Delay / component.BaseDelay); + } + private void SetEnabled(EntityUid uid, CargoTelepadComponent component, ApcPowerReceiverComponent? receiver = null, TransformComponent? xform = null) { diff --git a/Content.Server/Chemistry/Components/SolutionHeaterComponent.cs b/Content.Server/Chemistry/Components/SolutionHeaterComponent.cs index c1841e022c7..bc1d44e82f1 100644 --- a/Content.Server/Chemistry/Components/SolutionHeaterComponent.cs +++ b/Content.Server/Chemistry/Components/SolutionHeaterComponent.cs @@ -4,8 +4,26 @@ namespace Content.Server.Chemistry.Components; public sealed partial class SolutionHeaterComponent : Component { /// - /// How much heat is added per second to the solution, taking upgrades into account. + /// How much heat is added per second to the solution, with no upgrades. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] + public float BaseHeatPerSecond = 120; + + /// + /// How much heat is added per second to the solution, taking upgrades into account. + /// + [ViewVariables(VVAccess.ReadWrite)] public float HeatPerSecond; + + /// + /// The machine part that affects the heat multiplier. + /// + [DataField] + public string MachinePartHeatMultiplier = "Capacitor"; + + /// + /// How much each upgrade multiplies the heat by. + /// + [DataField] + public float PartRatingHeatMultiplier = 1.5f; } diff --git a/Content.Server/Chemistry/EntitySystems/SolutionHeaterSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionHeaterSystem.cs index 6e6373e10bf..1ef589ab5cb 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionHeaterSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionHeaterSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Construction; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Chemistry; @@ -20,6 +21,8 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnItemPlaced); SubscribeLocalEvent(OnItemRemoved); } @@ -61,6 +64,18 @@ private void OnPowerChanged(Entity entity, ref PowerCha } } + private void OnRefreshParts(Entity entity, ref RefreshPartsEvent args) + { + var heatRating = args.PartRatings[entity.Comp.MachinePartHeatMultiplier] - 1; + + entity.Comp.HeatPerSecond = entity.Comp.BaseHeatPerSecond * MathF.Pow(entity.Comp.PartRatingHeatMultiplier, heatRating); + } + + private void OnUpgradeExamine(Entity entity, ref UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("solution-heater-upgrade-heat", entity.Comp.HeatPerSecond / entity.Comp.BaseHeatPerSecond); + } + private void OnItemPlaced(Entity entity, ref ItemPlacedEvent args) { TryTurnOn(entity); diff --git a/Content.Server/Chemistry/ReagentEffects/MakeSentient.cs b/Content.Server/Chemistry/ReagentEffects/MakeSentient.cs index 5654f9067b5..a84e21b997e 100644 --- a/Content.Server/Chemistry/ReagentEffects/MakeSentient.cs +++ b/Content.Server/Chemistry/ReagentEffects/MakeSentient.cs @@ -41,7 +41,7 @@ public override void Effect(ReagentEffectArgs args) if (!knowledge.SpokenLanguages.Contains(fallback)) knowledge.SpokenLanguages.Add(fallback); - IoCManager.Resolve().GetEntitySystem().UpdateEntityLanguages(uid, speaker); + IoCManager.Resolve().GetEntitySystem().UpdateEntityLanguages(uid); // Stops from adding a ghost role to things like people who already have a mind if (entityManager.TryGetComponent(uid, out var mindContainer) && mindContainer.HasMind) diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index 7931fae4778..72104bc381f 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Chat.Systems; using Content.Server.Cloning.Components; +using Content.Server.Construction; using Content.Server.DeviceLinking.Systems; using Content.Server.EUI; using Content.Server.Fluids.EntitySystems; @@ -9,6 +10,10 @@ using Content.Server.Materials; using Content.Server.Popups; using Content.Server.Power.EntitySystems; +using Content.Server.Traits.Assorted; +using Content.Shared.Atmos; +using Content.Shared.CCVar; +using Content.Shared.Chemistry.Components; using Content.Shared.Cloning; using Content.Shared.Damage; using Content.Shared.DeviceLinking.Events; @@ -90,8 +95,23 @@ public override void Initialize() SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnPartsRefreshed); + SubscribeLocalEvent(OnUpgradeExamine); + } + private void OnPartsRefreshed(EntityUid uid, CloningPodComponent component, RefreshPartsEvent args) + { + var materialRating = args.PartRatings[component.MachinePartMaterialUse]; + var speedRating = args.PartRatings[component.MachinePartCloningSpeed]; + + component.BiomassCostMultiplier = MathF.Pow(component.PartRatingMaterialMultiplier, materialRating - 1); + component.CloningTime = component.CloningTime * MathF.Pow(component.PartRatingSpeedMultiplier, speedRating - 1); } + private void OnUpgradeExamine(EntityUid uid, CloningPodComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("cloning-pod-component-upgrade-speed", component.CloningTime / component.CloningTime); + args.AddPercentageUpgrade("cloning-pod-component-upgrade-biomass-requirement", component.BiomassCostMultiplier); + } private void OnPortDisconnected(EntityUid uid, CloningPodComponent pod, PortDisconnectedEvent args) { pod.ConnectedConsole = null; diff --git a/Content.Server/Construction/ConstructionSystem.Machine.cs b/Content.Server/Construction/ConstructionSystem.Machine.cs index 2e670dbe40d..65b0b704761 100644 --- a/Content.Server/Construction/ConstructionSystem.Machine.cs +++ b/Content.Server/Construction/ConstructionSystem.Machine.cs @@ -5,6 +5,7 @@ using Content.Shared.Construction.Prototypes; using Content.Shared.Verbs; using Robust.Shared.Containers; +using Robust.Shared.Map.Components; using Robust.Shared.Utility; namespace Content.Server.Construction; @@ -143,7 +144,7 @@ private void CreateBoardAndStockParts(EntityUid uid, MachineComponent component) var p = EntityManager.SpawnEntity(partProto.StockPartPrototype, xform.Coordinates); if (!_container.Insert(p, partContainer)) - throw new Exception($"Couldn't insert machine part of type {part} to machine with prototype {partProto.StockPartPrototype}!"); + throw new Exception($"Couldn't insert machine part of type {part} to machine with prototype {partProto.StockPartPrototype ?? "N/A"}!"); } } @@ -183,7 +184,7 @@ public sealed class RefreshPartsEvent : EntityEventArgs { public IReadOnlyList Parts = new List(); - public Dictionary PartRatings = new(); + public Dictionary PartRatings = new Dictionary(); } public sealed class UpgradeExamineEvent : EntityEventArgs diff --git a/Content.Server/Construction/PartExchangerSystem.cs b/Content.Server/Construction/PartExchangerSystem.cs index ee5edcbd0a0..f364d1b547d 100644 --- a/Content.Server/Construction/PartExchangerSystem.cs +++ b/Content.Server/Construction/PartExchangerSystem.cs @@ -10,6 +10,7 @@ using Robust.Shared.Containers; using Robust.Shared.Utility; using Content.Shared.Wires; +using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Collections; @@ -42,7 +43,7 @@ private void OnDoAfter(EntityUid uid, PartExchangerComponent component, DoAfterE if (args.Handled || args.Args.Target == null) return; - if (!TryComp(uid, out var storage)) + if (!TryComp(uid, out var storage) || storage.Container == null) return; //the parts are stored in here var machinePartQuery = GetEntityQuery(); diff --git a/Content.Server/Flight/FlightSystem.cs b/Content.Server/Flight/FlightSystem.cs index e056fc24ec0..39321b1e66c 100644 --- a/Content.Server/Flight/FlightSystem.cs +++ b/Content.Server/Flight/FlightSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Flight.Events; using Content.Shared.Mobs; using Content.Shared.Popups; +using Content.Shared.Standing; using Content.Shared.Stunnable; using Content.Shared.Zombies; using Robust.Shared.Audio.Systems; @@ -16,6 +17,7 @@ public sealed class FlightSystem : SharedFlightSystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; public override void Initialize() { @@ -27,6 +29,7 @@ public override void Initialize() SubscribeLocalEvent(OnZombified); SubscribeLocalEvent(OnKnockedDown); SubscribeLocalEvent(OnStunned); + SubscribeLocalEvent(OnDowned); SubscribeLocalEvent(OnSleep); } public override void Update(float frameTime) @@ -103,6 +106,13 @@ private bool CanFly(EntityUid uid, FlightComponent component) _popupSystem.PopupEntity(Loc.GetString("no-flight-while-zombified"), uid, uid, PopupType.Medium); return false; } + + if (HasComp(uid) && _standing.IsDown(uid)) + { + _popupSystem.PopupEntity(Loc.GetString("no-flight-while-lying"), uid, uid, PopupType.Medium); + return false; + } + return true; } @@ -142,6 +152,14 @@ private void OnStunned(EntityUid uid, FlightComponent component, ref StunnedEven ToggleActive(uid, false, component); } + private void OnDowned(EntityUid uid, FlightComponent component, ref DownedEvent args) + { + if (!component.On) + return; + + ToggleActive(uid, false, component); + } + private void OnSleep(EntityUid uid, FlightComponent component, ref SleepStateChangedEvent args) { if (!component.On diff --git a/Content.Server/Gravity/GravityGeneratorComponent.cs b/Content.Server/Gravity/GravityGeneratorComponent.cs index f9462920384..f47d3979391 100644 --- a/Content.Server/Gravity/GravityGeneratorComponent.cs +++ b/Content.Server/Gravity/GravityGeneratorComponent.cs @@ -37,6 +37,9 @@ public sealed partial class GravityGeneratorComponent : SharedGravityGeneratorCo // 0 -> 1 [ViewVariables(VVAccess.ReadWrite)] [DataField("charge")] public float Charge { get; set; } = 1; + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartMaxChargeMultiplier = "Capacitor"; + /// /// Is the gravity generator currently "producing" gravity? /// diff --git a/Content.Server/Gravity/GravityGeneratorSystem.cs b/Content.Server/Gravity/GravityGeneratorSystem.cs index ec5646457e2..b1696e6a713 100644 --- a/Content.Server/Gravity/GravityGeneratorSystem.cs +++ b/Content.Server/Gravity/GravityGeneratorSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Administration.Logs; using Content.Server.Audio; +using Content.Server.Construction; using Content.Server.Power.Components; using Content.Server.Emp; using Content.Shared.Database; @@ -27,6 +28,7 @@ public override void Initialize() SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(OnParentChanged); // Or just anchor changed? SubscribeLocalEvent(OnInteractHand); + SubscribeLocalEvent(OnRefreshParts); SubscribeLocalEvent( OnSwitchGenerator); @@ -257,6 +259,12 @@ public void UpdateState(Entity ent, AppearanceComponent? appearance) { _ambientSoundSystem.SetAmbience(ent, false); diff --git a/Content.Server/Kitchen/Components/MicrowaveComponent.cs b/Content.Server/Kitchen/Components/MicrowaveComponent.cs index 815ba8f5213..1e343e5e332 100644 --- a/Content.Server/Kitchen/Components/MicrowaveComponent.cs +++ b/Content.Server/Kitchen/Components/MicrowaveComponent.cs @@ -11,95 +11,99 @@ namespace Content.Server.Kitchen.Components [RegisterComponent] public sealed partial class MicrowaveComponent : Component { - [DataField("cookTimeMultiplier"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float CookTimeMultiplier = 1; - - [DataField("baseHeatMultiplier"), ViewVariables(VVAccess.ReadWrite)] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartCookTimeMultiplier = "Capacitor"; + [DataField] + public float CookTimeScalingConstant = 0.5f; + [DataField] public float BaseHeatMultiplier = 100; - [DataField("objectHeatMultiplier"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float ObjectHeatMultiplier = 100; - [DataField("failureResult", customTypeSerializer: typeof(PrototypeIdSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string BadRecipeEntityId = "FoodBadRecipe"; #region audio - [DataField("beginCookingSound")] + [DataField] public SoundSpecifier StartCookingSound = new SoundPathSpecifier("/Audio/Machines/microwave_start_beep.ogg"); - [DataField("foodDoneSound")] + [DataField] public SoundSpecifier FoodDoneSound = new SoundPathSpecifier("/Audio/Machines/microwave_done_beep.ogg"); - [DataField("clickSound")] + [DataField] public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); - [DataField("ItemBreakSound")] + [DataField] public SoundSpecifier ItemBreakSound = new SoundPathSpecifier("/Audio/Effects/clang.ogg"); public EntityUid? PlayingStream; - [DataField("loopingSound")] + [DataField] public SoundSpecifier LoopingSound = new SoundPathSpecifier("/Audio/Machines/microwave_loop.ogg"); #endregion [ViewVariables] public bool Broken; - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public ProtoId OnPort = "On"; /// - /// This is a fixed offset of 5. - /// The cook times for all recipes should be divisible by 5,with a minimum of 1 second. - /// For right now, I don't think any recipe cook time should be greater than 60 seconds. + /// This is a fixed offset of 5. + /// The cook times for all recipes should be divisible by 5,with a minimum of 1 second. + /// For right now, I don't think any recipe cook time should be greater than 60 seconds. /// - [DataField("currentCookTimerTime"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public uint CurrentCookTimerTime = 0; /// - /// Tracks the elapsed time of the current cook timer. + /// Tracks the elapsed time of the current cook timer. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan CurrentCookTimeEnd = TimeSpan.Zero; /// - /// The maximum number of seconds a microwave can be set to. - /// This is currently only used for validation and the client does not check this. + /// The maximum number of seconds a microwave can be set to. + /// This is currently only used for validation and the client does not check this. /// - [DataField("maxCookTime"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public uint MaxCookTime = 30; /// /// The max temperature that this microwave can heat objects to. /// - [DataField("temperatureUpperThreshold")] + [DataField] public float TemperatureUpperThreshold = 373.15f; public int CurrentCookTimeButtonIndex; public Container Storage = default!; - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public int Capacity = 10; - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public ProtoId MaxItemSize = "Normal"; /// - /// How frequently the microwave can malfunction. + /// How frequently the microwave can malfunction. /// [DataField] public float MalfunctionInterval = 1.0f; /// - /// Chance of an explosion occurring when we microwave a metallic object + /// Chance of an explosion occurring when we microwave a metallic object /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float ExplosionChance = .1f; /// - /// Chance of lightning occurring when we microwave a metallic object - [DataField, ViewVariables(VVAccess.ReadWrite)] + /// Chance of lightning occurring when we microwave a metallic object + /// + [DataField] public float LightningChance = .75f; } diff --git a/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs b/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs index 5bbbe2dc8da..4f4531206c7 100644 --- a/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs +++ b/Content.Server/Kitchen/Components/ReagentGrinderComponent.cs @@ -1,6 +1,8 @@ using Content.Shared.Kitchen; using Content.Server.Kitchen.EntitySystems; +using Content.Shared.Construction.Prototypes; using Robust.Shared.Audio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Kitchen.Components { @@ -13,15 +15,30 @@ namespace Content.Server.Kitchen.Components [Access(typeof(ReagentGrinderSystem)), RegisterComponent] public sealed partial class ReagentGrinderComponent : Component { - [DataField] + [ViewVariables(VVAccess.ReadWrite)] public int StorageMaxEntities = 6; [DataField] - public TimeSpan WorkTime = TimeSpan.FromSeconds(3.5); // Roughly matches the grind/juice sounds. + public int BaseStorageMaxEntities = 4; + + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartStorageMax = "MatterBin"; + + [DataField] + public int StoragePerPartRating = 4; [DataField] + public TimeSpan WorkTime = TimeSpan.FromSeconds(3.5); // Roughly matches the grind/juice sounds. + + [ViewVariables(VVAccess.ReadWrite)] public float WorkTimeMultiplier = 1; + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartWorkTime = "Manipulator"; + + [DataField] + public float PartRatingWorkTimerMulitplier = 0.6f; + [DataField] public SoundSpecifier ClickSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg"); diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs index 212383c463a..3de7051f54d 100644 --- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs @@ -76,6 +76,8 @@ public override void Initialize() SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnAnchorChanged); SubscribeLocalEvent(OnSuicide); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnSignalReceived); @@ -342,6 +344,17 @@ private void OnAnchorChanged(EntityUid uid, MicrowaveComponent component, ref An _container.EmptyContainer(component.Storage); } + private void OnRefreshParts(Entity ent, ref RefreshPartsEvent args) + { + var cookRating = args.PartRatings[ent.Comp.MachinePartCookTimeMultiplier]; + ent.Comp.CookTimeMultiplier = MathF.Pow(ent.Comp.CookTimeScalingConstant, cookRating - 1); + } + + private void OnUpgradeExamine(Entity ent, ref UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("microwave-component-upgrade-cook-time", ent.Comp.CookTimeMultiplier); + } + private void OnSignalReceived(Entity ent, ref SignalReceivedEvent args) { if (args.Port != ent.Comp.OnPort) diff --git a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs index e8ee4539860..aad33fea678 100644 --- a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Construction; using Content.Server.Kitchen.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -48,6 +49,8 @@ public override void Initialize() SubscribeLocalEvent((uid, _, _) => UpdateUiState(uid)); SubscribeLocalEvent((EntityUid uid, ReagentGrinderComponent _, ref PowerChangedEvent _) => UpdateUiState(uid)); SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnContainerModified); SubscribeLocalEvent(OnContainerModified); @@ -197,6 +200,24 @@ private void OnInteractUsing(Entity entity, ref Interac args.Handled = true; } + /// + /// Gotta be efficient, you know? you're saving a whole extra second here and everything. + /// + private void OnRefreshParts(Entity entity, ref RefreshPartsEvent args) + { + var ratingWorkTime = args.PartRatings[entity.Comp.MachinePartWorkTime]; + var ratingStorage = args.PartRatings[entity.Comp.MachinePartStorageMax]; + + entity.Comp.WorkTimeMultiplier = MathF.Pow(entity.Comp.PartRatingWorkTimerMulitplier, ratingWorkTime - 1); + entity.Comp.StorageMaxEntities = entity.Comp.BaseStorageMaxEntities + (int) (entity.Comp.StoragePerPartRating * (ratingStorage - 1)); + } + + private void OnUpgradeExamine(Entity entity, ref UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("reagent-grinder-component-upgrade-work-time", entity.Comp.WorkTimeMultiplier); + args.AddNumberUpgrade("reagent-grinder-component-upgrade-storage", entity.Comp.StorageMaxEntities - entity.Comp.BaseStorageMaxEntities); + } + private void UpdateUiState(EntityUid uid) { ReagentGrinderComponent? grinderComp = null; diff --git a/Content.Server/Language/LanguageSystem.Networking.cs b/Content.Server/Language/LanguageSystem.Networking.cs index 572e2961fde..5f7f2742734 100644 --- a/Content.Server/Language/LanguageSystem.Networking.cs +++ b/Content.Server/Language/LanguageSystem.Networking.cs @@ -64,12 +64,7 @@ private void SendLanguageStateToClient(ICommonSession session, LanguageSpeakerCo // TODO this is really stupid and can be avoided if we just make everything shared... private void SendLanguageStateToClient(EntityUid uid, ICommonSession session, LanguageSpeakerComponent? component = null) { - var isUniversal = HasComp(uid); - if (!isUniversal) - Resolve(uid, ref component, logMissing: false); - - // I really don't want to call 3 getter methods here, so we'll just have this slightly hardcoded solution - var message = isUniversal || component == null + var message = !Resolve(uid, ref component, logMissing: false) ? new LanguagesUpdatedMessage(UniversalPrototype, [UniversalPrototype], [UniversalPrototype]) : new LanguagesUpdatedMessage(component.CurrentLanguage, component.SpokenLanguages, component.UnderstoodLanguages); diff --git a/Content.Server/Language/LanguageSystem.cs b/Content.Server/Language/LanguageSystem.cs index e68489e9e28..a1c30997e2d 100644 --- a/Content.Server/Language/LanguageSystem.cs +++ b/Content.Server/Language/LanguageSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Language.Events; using Content.Shared.Language; using Content.Shared.Language.Components; -using Content.Shared.Language.Events; using Content.Shared.Language.Systems; using UniversalLanguageSpeakerComponent = Content.Shared.Language.Components.UniversalLanguageSpeakerComponent; @@ -16,8 +15,19 @@ public override void Initialize() InitializeNet(); SubscribeLocalEvent(OnInitLanguageSpeaker); + SubscribeLocalEvent(OnUniversalInit); + SubscribeLocalEvent(OnUniversalShutdown); } + private void OnUniversalShutdown(EntityUid uid, UniversalLanguageSpeakerComponent component, ComponentShutdown args) + { + RemoveLanguage(uid, UniversalPrototype); + } + + private void OnUniversalInit(EntityUid uid, UniversalLanguageSpeakerComponent component, MapInitEvent args) + { + AddLanguage(uid, UniversalPrototype); + } #region public api @@ -48,10 +58,9 @@ public bool CanSpeak(EntityUid speaker, string language, LanguageSpeakerComponen /// public LanguagePrototype GetLanguage(EntityUid speaker, LanguageSpeakerComponent? component = null) { - if (HasComp(speaker) || !Resolve(speaker, ref component, logMissing: false)) - return Universal; // Serves both as a fallback and uhhh something (TODO: fix this comment) - - if (string.IsNullOrEmpty(component.CurrentLanguage) || !_prototype.TryIndex(component.CurrentLanguage, out var proto)) + if (!Resolve(speaker, ref component, logMissing: false) + || string.IsNullOrEmpty(component.CurrentLanguage) + || !_prototype.TryIndex(component.CurrentLanguage, out var proto)) return Universal; return proto; @@ -63,13 +72,10 @@ public LanguagePrototype GetLanguage(EntityUid speaker, LanguageSpeakerComponent /// Typically, checking is sufficient. public List GetSpokenLanguages(EntityUid uid) { - if (HasComp(uid)) - return [UniversalPrototype]; + if (!TryComp(uid, out var component)) + return []; - if (TryComp(uid, out var component)) - return component.SpokenLanguages; - - return []; + return component.SpokenLanguages; } /// @@ -78,21 +84,17 @@ public List GetSpokenLanguages(EntityUid uid) /// Typically, checking is sufficient. public List GetUnderstoodLanguages(EntityUid uid) { - if (HasComp(uid)) - return [UniversalPrototype]; // This one is tricky because... well, they understand all of them, not just one. - - if (TryComp(uid, out var component)) - return component.UnderstoodLanguages; + if (!TryComp(uid, out var component)) + return []; - return []; + return component.UnderstoodLanguages; } public void SetLanguage(EntityUid speaker, string language, LanguageSpeakerComponent? component = null) { - if (!CanSpeak(speaker, language) || (HasComp(speaker) && language != UniversalPrototype)) - return; - - if (!Resolve(speaker, ref component) || component.CurrentLanguage == language) + if (!CanSpeak(speaker, language) + || !Resolve(speaker, ref component) + || component.CurrentLanguage == language) return; component.CurrentLanguage = language; @@ -106,12 +108,10 @@ public void AddLanguage( EntityUid uid, string language, bool addSpoken = true, - bool addUnderstood = true, - LanguageKnowledgeComponent? knowledge = null, - LanguageSpeakerComponent? speaker = null) + bool addUnderstood = true) { - if (knowledge == null) - knowledge = EnsureComp(uid); + EnsureComp(uid, out var knowledge); + EnsureComp(uid); if (addSpoken && !knowledge.SpokenLanguages.Contains(language)) knowledge.SpokenLanguages.Add(language); @@ -119,7 +119,7 @@ public void AddLanguage( if (addUnderstood && !knowledge.UnderstoodLanguages.Contains(language)) knowledge.UnderstoodLanguages.Add(language); - UpdateEntityLanguages(uid, speaker); + UpdateEntityLanguages(uid); } /// @@ -129,12 +129,10 @@ public void RemoveLanguage( EntityUid uid, string language, bool removeSpoken = true, - bool removeUnderstood = true, - LanguageKnowledgeComponent? knowledge = null, - LanguageSpeakerComponent? speaker = null) + bool removeUnderstood = true) { - if (knowledge == null) - knowledge = EnsureComp(uid); + if (!TryComp(uid, out var knowledge)) + return; if (removeSpoken) knowledge.SpokenLanguages.Remove(language); @@ -142,7 +140,7 @@ public void RemoveLanguage( if (removeUnderstood) knowledge.UnderstoodLanguages.Remove(language); - UpdateEntityLanguages(uid, speaker); + UpdateEntityLanguages(uid); } /// @@ -168,9 +166,9 @@ public bool EnsureValidLanguage(EntityUid entity, LanguageSpeakerComponent? comp /// /// Immediately refreshes the cached lists of spoken and understood languages for the given entity. /// - public void UpdateEntityLanguages(EntityUid entity, LanguageSpeakerComponent? languages = null) + public void UpdateEntityLanguages(EntityUid entity) { - if (!Resolve(entity, ref languages)) + if (!TryComp(entity, out var languages)) return; var ev = new DetermineEntityLanguagesEvent(); @@ -205,7 +203,7 @@ private void OnInitLanguageSpeaker(EntityUid uid, LanguageSpeakerComponent compo if (string.IsNullOrEmpty(component.CurrentLanguage)) component.CurrentLanguage = component.SpokenLanguages.FirstOrDefault(UniversalPrototype); - UpdateEntityLanguages(uid, component); + UpdateEntityLanguages(uid); } #endregion diff --git a/Content.Server/Language/TranslatorSystem.cs b/Content.Server/Language/TranslatorSystem.cs index a893993e884..c48b93a3930 100644 --- a/Content.Server/Language/TranslatorSystem.cs +++ b/Content.Server/Language/TranslatorSystem.cs @@ -85,8 +85,8 @@ private void OnTranslatorParentChanged(EntityUid translator, HandheldTranslatorC // If that is not the case, then OnProxyDetermineLanguages will remove this translator from HoldsTranslatorComponent.Translators. Timer.Spawn(0, () => { - if (Exists(args.OldParent) && TryComp(args.OldParent, out var speaker)) - _language.UpdateEntityLanguages(args.OldParent.Value, speaker); + if (Exists(args.OldParent) && HasComp(args.OldParent)) + _language.UpdateEntityLanguages(args.OldParent.Value); }); } @@ -108,7 +108,7 @@ private void OnTranslatorToggle(EntityUid translator, HandheldTranslatorComponen { // The first new spoken language added by this translator, or null var firstNewLanguage = translatorComp.SpokenLanguages.FirstOrDefault(it => !languageComp.SpokenLanguages.Contains(it)); - _language.UpdateEntityLanguages(holder, languageComp); + _language.UpdateEntityLanguages(holder); // Update the current language of the entity if necessary if (isEnabled && translatorComp.SetLanguageOnInteract && firstNewLanguage is {}) @@ -131,8 +131,8 @@ private void OnPowerCellSlotEmpty(EntityUid translator, HandheldTranslatorCompon _powerCell.SetPowerCellDrawEnabled(translator, false); OnAppearanceChange(translator, component); - if (_containers.TryGetContainingContainer(translator, out var holderCont) && TryComp(holderCont.Owner, out var languageComp)) - _language.UpdateEntityLanguages(holderCont.Owner, languageComp); + if (_containers.TryGetContainingContainer(translator, out var holderCont) && HasComp(holderCont.Owner)) + _language.UpdateEntityLanguages(holderCont.Owner); } private void CopyLanguages(BaseTranslatorComponent from, DetermineEntityLanguagesEvent to, LanguageKnowledgeComponent knowledge) diff --git a/Content.Server/Materials/MaterialReclaimerSystem.cs b/Content.Server/Materials/MaterialReclaimerSystem.cs index aa24fde44b7..de82f125985 100644 --- a/Content.Server/Materials/MaterialReclaimerSystem.cs +++ b/Content.Server/Materials/MaterialReclaimerSystem.cs @@ -1,4 +1,6 @@ using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Chemistry.EntitySystems; +using Content.Server.Construction; using Content.Server.Fluids.EntitySystems; using Content.Server.GameTicking; using Content.Server.Popups; @@ -45,6 +47,8 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnInteractUsing, before: new []{typeof(WiresSystem), typeof(SolutionTransferSystem)}); @@ -56,6 +60,18 @@ private void OnStartup(Entity entity, ref ComponentS _solutionContainer.EnsureSolution(entity.Owner, entity.Comp.SolutionContainerId); } + private void OnUpgradeExamine(Entity entity, ref UpgradeExamineEvent args) + { + args.AddPercentageUpgrade(Loc.GetString("material-reclaimer-upgrade-process-rate"), entity.Comp.MaterialProcessRate / entity.Comp.BaseMaterialProcessRate); + } + + private void OnRefreshParts(Entity entity, ref RefreshPartsEvent args) + { + var rating = args.PartRatings[entity.Comp.MachinePartProcessRate] - 1; + entity.Comp.MaterialProcessRate = entity.Comp.BaseMaterialProcessRate * MathF.Pow(entity.Comp.PartRatingProcessRateMultiplier, rating); + Dirty(entity); + } + private void OnPowerChanged(Entity entity, ref PowerChangedEvent args) { AmbientSound.SetAmbience(entity.Owner, entity.Comp.Enabled && args.Powered); diff --git a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerComponent.cs b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerComponent.cs index 61d36f98b96..1358bfbcbbc 100644 --- a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerComponent.cs +++ b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerComponent.cs @@ -1,4 +1,8 @@ +using System.Threading; +using Content.Shared.Construction.Prototypes; using Content.Shared.Storage; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Medical.BiomassReclaimer { @@ -6,72 +10,111 @@ namespace Content.Server.Medical.BiomassReclaimer public sealed partial class BiomassReclaimerComponent : Component { /// - /// This gets set for each mob it processes. - /// When it hits 0, there is a chance for the reclaimer to either spill blood or throw an item. + /// This gets set for each mob it processes. + /// When it hits 0, there is a chance for the reclaimer to either spill blood or throw an item. /// [ViewVariables] public float RandomMessTimer = 0f; /// - /// The interval for . + /// The interval for . /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField] public TimeSpan RandomMessInterval = TimeSpan.FromSeconds(5); /// - /// This gets set for each mob it processes. - /// When it hits 0, spit out biomass. + /// This gets set for each mob it processes. + /// When it hits 0, spit out biomass. /// [ViewVariables] - public float ProcessingTimer = default; + public float ProcessingTimer; /// - /// Amount of biomass that the mob being processed will yield. - /// This is calculated from the YieldPerUnitMass. - /// Also stores non-integer leftovers. + /// Amount of biomass that the mob being processed will yield. + /// This is calculated from the YieldPerUnitMass. + /// Also stores non-integer leftovers. /// [ViewVariables] - public float CurrentExpectedYield = 0f; + public float CurrentExpectedYield; /// - /// The reagent that will be spilled while processing a mob. + /// The reagent that will be spilled while processing a mob. /// [ViewVariables] public string? BloodReagent; /// - /// Entities that can be randomly spawned while processing a mob. + /// Entities that can be randomly spawned while processing a mob. /// public List SpawnedEntities = new(); /// - /// How many units of biomass it produces for each unit of mass. + /// How many units of biomass it produces for each unit of mass. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float YieldPerUnitMass = 0.4f; + [ViewVariables(VVAccess.ReadWrite)] + public float YieldPerUnitMass = default; /// - /// How many seconds to take to insert an entity per unit of its mass. + /// The base yield per mass unit when no components are upgraded. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] + public float BaseYieldPerUnitMass = 0.4f; + + /// + /// Machine part whose rating modifies the yield per mass. + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartYieldAmount = "MatterBin"; + + /// + /// How much the machine part quality affects the yield. + /// Going up a tier will multiply the yield by this amount. + /// + [DataField] + public float PartRatingYieldAmountMultiplier = 1.25f; + + /// + /// How many seconds to take to insert an entity per unit of its mass. + /// + [DataField] public float BaseInsertionDelay = 0.1f; /// - /// How much to multiply biomass yield from botany produce. + /// How much to multiply biomass yield from botany produce. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float ProduceYieldMultiplier = 0.25f; /// - /// The time it takes to process a mob, per mass. + /// The time it takes to process a mob, per mass. + /// + [ViewVariables(VVAccess.ReadWrite)] + public float ProcessingTimePerUnitMass; + + /// + /// The base time per mass unit that it takes to process a mob + /// when no components are upgraded. + /// + [DataField] + public float BaseProcessingTimePerUnitMass = 0.5f; + + /// + /// The machine part that increses the processing speed. + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartProcessingSpeed = "Manipulator"; + + /// + /// How much the machine part quality affects the yield. + /// Going up a tier will multiply the speed by this amount. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float ProcessingTimePerUnitMass = 0.5f; + [DataField] + public float PartRatingSpeedMultiplier = 1.35f; /// - /// Will this refuse to gib a living mob? + /// Will this refuse to gib a living mob? /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField] public bool SafetyEnabled = true; } } diff --git a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs index eaf04d64b2b..97a758a5ed3 100644 --- a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs +++ b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs @@ -1,6 +1,7 @@ using System.Numerics; using Content.Server.Body.Components; using Content.Server.Botany.Components; +using Content.Server.Construction; using Content.Server.Fluids.EntitySystems; using Content.Server.Materials; using Content.Server.Power.Components; @@ -99,6 +100,8 @@ public override void Initialize() SubscribeLocalEvent(OnUnanchorAttempt); SubscribeLocalEvent(OnAfterInteractUsing); SubscribeLocalEvent(OnClimbedOn); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnSuicide); SubscribeLocalEvent(OnDoAfter); @@ -173,6 +176,26 @@ private void OnClimbedOn(Entity reclaimer, ref Climbe StartProcessing(args.Climber, reclaimer); } + private void OnRefreshParts(EntityUid uid, BiomassReclaimerComponent component, RefreshPartsEvent args) + { + var laserRating = args.PartRatings[component.MachinePartProcessingSpeed]; + var manipRating = args.PartRatings[component.MachinePartYieldAmount]; + + // Processing time slopes downwards with part rating. + component.ProcessingTimePerUnitMass = + component.BaseProcessingTimePerUnitMass / MathF.Pow(component.PartRatingSpeedMultiplier, laserRating - 1); + + // Yield slopes upwards with part rating. + component.YieldPerUnitMass = + component.BaseYieldPerUnitMass * MathF.Pow(component.PartRatingYieldAmountMultiplier, manipRating - 1); + } + + private void OnUpgradeExamine(EntityUid uid, BiomassReclaimerComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("biomass-reclaimer-component-upgrade-speed", component.BaseProcessingTimePerUnitMass / component.ProcessingTimePerUnitMass); + args.AddPercentageUpgrade("biomass-reclaimer-component-upgrade-biomass-yield", component.YieldPerUnitMass / component.BaseYieldPerUnitMass); + } + private void OnDoAfter(Entity reclaimer, ref ReclaimerDoAfterEvent args) { if (args.Handled diff --git a/Content.Server/Medical/Components/MedicalScannerComponent.cs b/Content.Server/Medical/Components/MedicalScannerComponent.cs index 96de6499875..15ca6cd2bd7 100644 --- a/Content.Server/Medical/Components/MedicalScannerComponent.cs +++ b/Content.Server/Medical/Components/MedicalScannerComponent.cs @@ -1,5 +1,4 @@ using Content.Shared.Construction.Prototypes; -using Content.Shared.DragDrop; using Content.Shared.MedicalScanner; using Robust.Shared.Containers; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -13,10 +12,15 @@ public sealed partial class MedicalScannerComponent : SharedMedicalScannerCompon public ContainerSlot BodyContainer = default!; public EntityUid? ConnectedConsole; - [DataField, ViewVariables(VVAccess.ReadWrite)] + [ViewVariables(VVAccess.ReadWrite)] public float CloningFailChanceMultiplier = 1f; - - // Nyano, needed for Metem Machine. + public float MetemKarmaBonus = 0.25f; + + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartCloningFailChance = "Capacitor"; + + [DataField] + public float PartRatingFailMultiplier = 0.75f; } } diff --git a/Content.Server/Medical/MedicalScannerSystem.cs b/Content.Server/Medical/MedicalScannerSystem.cs index a6ce43c4081..ab6918e373b 100644 --- a/Content.Server/Medical/MedicalScannerSystem.cs +++ b/Content.Server/Medical/MedicalScannerSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Verbs; using Robust.Shared.Containers; using Content.Server.Cloning.Components; +using Content.Server.Construction; using Content.Server.DeviceLinking.Systems; using Content.Shared.DeviceLinking.Events; using Content.Server.Power.EntitySystems; @@ -44,6 +45,8 @@ public override void Initialize() SubscribeLocalEvent(OnDragDropOn); SubscribeLocalEvent(OnPortDisconnected); SubscribeLocalEvent(OnAnchorChanged); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnCanDragDropOn); } @@ -220,5 +223,17 @@ public void EjectBody(EntityUid uid, MedicalScannerComponent? scannerComponent) _climbSystem.ForciblySetClimbing(contained, uid); UpdateAppearance(uid, scannerComponent); } + + private void OnRefreshParts(EntityUid uid, MedicalScannerComponent component, RefreshPartsEvent args) + { + var ratingFail = args.PartRatings[component.MachinePartCloningFailChance]; + + component.CloningFailChanceMultiplier = MathF.Pow(component.PartRatingFailMultiplier, ratingFail - 1); + } + + private void OnUpgradeExamine(EntityUid uid, MedicalScannerComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("medical-scanner-upgrade-cloning", component.CloningFailChanceMultiplier); + } } } diff --git a/Content.Server/NPC/Components/NPCJukeComponent.cs b/Content.Server/NPC/Components/NPCJukeComponent.cs index 2c4136c24b9..768feeca6fc 100644 --- a/Content.Server/NPC/Components/NPCJukeComponent.cs +++ b/Content.Server/NPC/Components/NPCJukeComponent.cs @@ -1,4 +1,3 @@ -using Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.NPC.Components; @@ -6,17 +5,20 @@ namespace Content.Server.NPC.Components; [RegisterComponent, AutoGenerateComponentPause] public sealed partial class NPCJukeComponent : Component { - [DataField("jukeType")] + [DataField] public JukeType JukeType = JukeType.Away; - [DataField("jukeDuration")] + [DataField] public float JukeDuration = 0.5f; - [DataField("nextJuke", customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField] + public float JukeCooldown = 3f; + + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] [AutoPausedField] public TimeSpan NextJuke; - [DataField("targetTile")] + [DataField] public Vector2i? TargetTile; } diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/JukeOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/JukeOperator.cs index 02a3b085104..68029f5a4c2 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/JukeOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/JukeOperator.cs @@ -6,17 +6,31 @@ public sealed partial class JukeOperator : HTNOperator, IHtnConditionalShutdown { [Dependency] private readonly IEntityManager _entManager = default!; - [DataField("jukeType")] + [DataField] public JukeType JukeType = JukeType.AdjacentTile; - [DataField("shutdownState")] + [DataField] public HTNPlanState ShutdownState { get; private set; } = HTNPlanState.PlanFinished; + /// + /// Controls how long(in seconds) the NPC will move while juking. + /// + [DataField] + public float JukeDuration = 0.5f; + + /// + /// Controls how often (in seconds) an NPC will try to juke. + /// + [DataField] + public float JukeCooldown = 3f; + public override void Startup(NPCBlackboard blackboard) { base.Startup(blackboard); var juke = _entManager.EnsureComponent(blackboard.GetValue(NPCBlackboard.Owner)); juke.JukeType = JukeType; + juke.JukeDuration = JukeDuration; + juke.JukeCooldown = JukeCooldown; } public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) diff --git a/Content.Server/NPC/Systems/NPCJukeSystem.cs b/Content.Server/NPC/Systems/NPCJukeSystem.cs index da9fa1f7615..5a724762ef6 100644 --- a/Content.Server/NPC/Systems/NPCJukeSystem.cs +++ b/Content.Server/NPC/Systems/NPCJukeSystem.cs @@ -3,6 +3,7 @@ using Content.Server.NPC.Events; using Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat; using Content.Server.Weapons.Melee; +using Content.Shared.Coordinates.Helpers; using Content.Shared.NPC; using Content.Shared.Weapons.Melee; using Robust.Shared.Collections; @@ -38,22 +39,19 @@ public override void Initialize() private void OnJukeSteering(EntityUid uid, NPCJukeComponent component, ref NPCSteeringEvent args) { - if (component.JukeType == JukeType.AdjacentTile) + if (_timing.CurTime < component.NextJuke) { - if (_npcRangedQuery.TryGetComponent(uid, out var ranged) && - ranged.Status == CombatStatus.NotInSight) - { - component.TargetTile = null; - return; - } + component.TargetTile = null; + return; + } - if (_timing.CurTime < component.NextJuke) - { - component.TargetTile = null; - return; - } + component.NextJuke = _timing.CurTime + TimeSpan.FromSeconds(component.JukeCooldown); - if (!TryComp(args.Transform.GridUid, out var grid)) + if (component.JukeType == JukeType.AdjacentTile) + { + if (_npcRangedQuery.TryGetComponent(uid, out var ranged) + && ranged.Status is CombatStatus.NotInSight + || !TryComp(args.Transform.GridUid, out var grid)) { component.TargetTile = null; return; @@ -107,12 +105,11 @@ private void OnJukeSteering(EntityUid uid, NPCJukeComponent component, ref NPCSt var elapsed = _timing.CurTime - component.NextJuke; - // Finished juke, reset timer. - if (elapsed.TotalSeconds > component.JukeDuration || - currentTile == component.TargetTile) + // Finished juke. + if (elapsed.TotalSeconds > component.JukeDuration + || currentTile == component.TargetTile) { component.TargetTile = null; - component.NextJuke = _timing.CurTime + TimeSpan.FromSeconds(component.JukeDuration); return; } @@ -155,9 +152,7 @@ private void OnJukeSteering(EntityUid uid, NPCJukeComponent component, ref NPCSt var obstacleDirection = _transform.GetWorldPosition(melee.Target) - args.WorldPosition; if (obstacleDirection == Vector2.Zero) - { obstacleDirection = _random.NextVector2(); - } // If they're moving away then pursue anyway. // If just hit then always back up a bit. diff --git a/Content.Server/Nutrition/Components/FatExtractorComponent.cs b/Content.Server/Nutrition/Components/FatExtractorComponent.cs index e23c557236c..fa6edc911e1 100644 --- a/Content.Server/Nutrition/Components/FatExtractorComponent.cs +++ b/Content.Server/Nutrition/Components/FatExtractorComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Nutrition.EntitySystems; +using Content.Shared.Construction.Prototypes; using Content.Shared.Nutrition.Components; using Robust.Shared.Audio; using Robust.Shared.Prototypes; @@ -8,67 +9,87 @@ namespace Content.Server.Nutrition.Components; /// -/// This is used for a machine that extracts hunger from entities and creates meat. Yum! +/// This is used for a machine that extracts hunger from entities and creates meat. Yum! /// [RegisterComponent, Access(typeof(FatExtractorSystem)), AutoGenerateComponentPause] public sealed partial class FatExtractorComponent : Component { /// - /// Whether or not the extractor is currently extracting fat from someone + /// Whether or not the extractor is currently extracting fat from someone /// - [DataField("processing")] + [DataField] public bool Processing = true; /// - /// How much nutrition is extracted per second. + /// How much nutrition is extracted per second. /// - [DataField("nutritionPerSecond"), ViewVariables(VVAccess.ReadWrite)] + [ViewVariables(VVAccess.ReadWrite)] public int NutritionPerSecond = 10; /// - /// An accumulator which tracks extracted nutrition to determine - /// when to spawn a meat. + /// The base rate of extraction /// - [DataField("nutrientAccumulator"), ViewVariables(VVAccess.ReadWrite)] + [DataField] + public int BaseNutritionPerSecond = 10; + + #region Machine Upgrade + /// + /// Which machine part affects the nutrition rate + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartNutritionRate = "Manipulator"; + + /// + /// The increase in rate per each rating above 1. + /// + [DataField] + public float PartRatingRateMultiplier = 10; + #endregion + + /// + /// An accumulator which tracks extracted nutrition to determine + /// when to spawn a meat. + /// + [DataField] public int NutrientAccumulator; /// - /// How high has to be to spawn meat + /// How high has to be to spawn meat /// - [DataField("nutrientPerMeat"), ViewVariables(VVAccess.ReadWrite)] - public int NutrientPerMeat = 30; + [DataField] + public int NutrientPerMeat = 60; /// - /// Meat spawned by the extractor. + /// Meat spawned by the extractor. /// - [DataField("meatPrototype", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string MeatPrototype = "FoodMeat"; /// - /// When the next update will occur + /// When the next update will occur /// - [DataField("nextUpdate", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] [AutoPausedField] public TimeSpan NextUpdate; /// - /// How long each update takes + /// How long each update takes /// - [DataField("updateTime"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan UpdateTime = TimeSpan.FromSeconds(1); /// - /// The sound played when extracting + /// The sound played when extracting /// - [DataField("processSound")] + [DataField] public SoundSpecifier? ProcessSound; public EntityUid? Stream; /// - /// A minium hunger threshold for extracting nutrition. - /// Ignored when emagged. + /// A minium hunger threshold for extracting nutrition. + /// Ignored when emagged. /// - [DataField("minHungerThreshold")] + [DataField] public HungerThreshold MinHungerThreshold = HungerThreshold.Okay; } diff --git a/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs b/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs index 180e40d1e42..dc1f67c7400 100644 --- a/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; +using Content.Server.Construction; using Content.Server.Nutrition.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -9,6 +10,7 @@ using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Storage.Components; +using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Timing; @@ -27,12 +29,25 @@ public sealed class FatExtractorSystem : EntitySystem /// public override void Initialize() { + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnGotEmagged); SubscribeLocalEvent(OnClosed); SubscribeLocalEvent(OnOpen); SubscribeLocalEvent(OnPowerChanged); } + private void OnRefreshParts(EntityUid uid, FatExtractorComponent component, RefreshPartsEvent args) + { + var rating = args.PartRatings[component.MachinePartNutritionRate] - 1; + component.NutritionPerSecond = component.BaseNutritionPerSecond + (int) (component.PartRatingRateMultiplier * rating); + } + + private void OnUpgradeExamine(EntityUid uid, FatExtractorComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("fat-extractor-component-rate", (float) component.NutritionPerSecond / component.BaseNutritionPerSecond); + } + private void OnGotEmagged(EntityUid uid, FatExtractorComponent component, ref GotEmaggedEvent args) { args.Handled = true; diff --git a/Content.Server/Nyanotrasen/Objectives/Components/BecomePsionicConditionComponent.cs b/Content.Server/Nyanotrasen/Objectives/Components/BecomePsionicConditionComponent.cs deleted file mode 100644 index 3b677bab2d4..00000000000 --- a/Content.Server/Nyanotrasen/Objectives/Components/BecomePsionicConditionComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Content.Server.Objectives.Systems; - -namespace Content.Server.Objectives.Components; - -/// -/// Requires that the player dies to be complete. -/// -[RegisterComponent, Access(typeof(BecomePsionicConditionSystem))] -public sealed partial class BecomePsionicConditionComponent : Component -{ -} \ No newline at end of file diff --git a/Content.Server/Nyanotrasen/Objectives/Systems/BecomePsionicConditionSystem.cs b/Content.Server/Nyanotrasen/Objectives/Systems/BecomePsionicConditionSystem.cs deleted file mode 100644 index d090c320a41..00000000000 --- a/Content.Server/Nyanotrasen/Objectives/Systems/BecomePsionicConditionSystem.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Content.Shared.Abilities.Psionics; -using Content.Server.Objectives.Components; -using Content.Shared.Mind; -using Content.Shared.Objectives.Components; - -namespace Content.Server.Objectives.Systems -{ - public sealed class BecomePsionicConditionSystem : EntitySystem - { - private EntityQuery _metaQuery; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnGetProgress); - } - - private void OnGetProgress(EntityUid uid, BecomePsionicConditionComponent comp, ref ObjectiveGetProgressEvent args) - { - args.Progress = GetProgress(args.Mind); - } - - private float GetProgress(MindComponent mind) - { - var entMan = IoCManager.Resolve(); - if (HasComp(mind.CurrentEntity)) - return 1; - return 0; - } - } -} diff --git a/Content.Server/Power/Components/UpgradeBatteryComponent.cs b/Content.Server/Power/Components/UpgradeBatteryComponent.cs new file mode 100644 index 00000000000..b676883b711 --- /dev/null +++ b/Content.Server/Power/Components/UpgradeBatteryComponent.cs @@ -0,0 +1,28 @@ +using Content.Shared.Construction.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Power.Components +{ + + [RegisterComponent] + public sealed partial class UpgradeBatteryComponent : Component + { + /// + /// The machine part that affects the power capacity. + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartPowerCapacity = "PowerCell"; + + /// + /// The machine part rating is raised to this power when calculating power gain + /// + [DataField] + public float MaxChargeMultiplier = 2f; + + /// + /// Power gain scaling + /// + [DataField] + public float BaseMaxCharge = 8000000; + } +} diff --git a/Content.Server/Power/Components/UpgradePowerDrawComponent.cs b/Content.Server/Power/Components/UpgradePowerDrawComponent.cs new file mode 100644 index 00000000000..23db4905cc5 --- /dev/null +++ b/Content.Server/Power/Components/UpgradePowerDrawComponent.cs @@ -0,0 +1,41 @@ +using Content.Server.Construction.Components; +using Content.Shared.Construction.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Power.Components; + +/// +/// This is used for machines whose power draw +/// can be decreased through machine part upgrades. +/// +[RegisterComponent] +public sealed partial class UpgradePowerDrawComponent : Component +{ + /// + /// The base power draw of the machine. + /// Prioritizes hv/mv draw over lv draw. + /// Value is initializezd on map init from + /// + [ViewVariables(VVAccess.ReadWrite)] + public float BaseLoad; + + /// + /// The machine part that affects the power draw. + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] + public string MachinePartPowerDraw = "Capacitor"; + + /// + /// The multiplier used for scaling the power draw. + /// + [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] + public float PowerDrawMultiplier = 1f; + + /// + /// What type of scaling is being used? + /// + [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] + public MachineUpgradeScalingType Scaling; +} + + diff --git a/Content.Server/Power/Components/UpgradePowerSupplierComponent.cs b/Content.Server/Power/Components/UpgradePowerSupplierComponent.cs new file mode 100644 index 00000000000..012c38a6b90 --- /dev/null +++ b/Content.Server/Power/Components/UpgradePowerSupplierComponent.cs @@ -0,0 +1,36 @@ +using Content.Server.Construction.Components; +using Content.Shared.Construction.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Power.Components; + +[RegisterComponent] +public sealed partial class UpgradePowerSupplierComponent : Component +{ + [ViewVariables(VVAccess.ReadWrite)] + public float BaseSupplyRate; + + /// + /// The machine part that affects the power supplu. + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartPowerSupply = "Capacitor"; + + /// + /// The multiplier used for scaling the power supply. + /// + [DataField(required: true)] + public float PowerSupplyMultiplier = 1f; + + /// + /// What type of scaling is being used? + /// + [DataField(required: true)] + public MachineUpgradeScalingType Scaling; + + /// + /// The current value that the power supply is being scaled by, + /// + [DataField] + public float ActualScalar = 1f; +} diff --git a/Content.Server/Power/Components/UpgradePowerSupplyRampingComponent.cs b/Content.Server/Power/Components/UpgradePowerSupplyRampingComponent.cs new file mode 100644 index 00000000000..61a654b383b --- /dev/null +++ b/Content.Server/Power/Components/UpgradePowerSupplyRampingComponent.cs @@ -0,0 +1,36 @@ +using Content.Server.Construction.Components; +using Content.Shared.Construction.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Power.Components; + +[RegisterComponent] +public sealed partial class UpgradePowerSupplyRampingComponent : Component +{ + [ViewVariables(VVAccess.ReadWrite)] + public float BaseRampRate; + + /// + /// The machine part that affects the power supply ramping + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartRampRate = "Capacitor"; + + /// + /// The multiplier used for scaling the power supply ramping + /// + [DataField] + public float SupplyRampingMultiplier = 1f; + + /// + /// What type of scaling is being used? + /// + [DataField(required: true)] + public MachineUpgradeScalingType Scaling; + + /// + /// The current value that the power supply is being scaled by + /// + [DataField] + public float ActualScalar = 1f; +} diff --git a/Content.Server/Power/EntitySystems/UpgradeBatterySystem.cs b/Content.Server/Power/EntitySystems/UpgradeBatterySystem.cs new file mode 100644 index 00000000000..734cf9d89ce --- /dev/null +++ b/Content.Server/Power/EntitySystems/UpgradeBatterySystem.cs @@ -0,0 +1,39 @@ +using Content.Server.Construction; +using Content.Server.Power.Components; +using JetBrains.Annotations; + +namespace Content.Server.Power.EntitySystems +{ + [UsedImplicitly] + public sealed class UpgradeBatterySystem : EntitySystem + { + [Dependency] private readonly BatterySystem _batterySystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); + } + + public void OnRefreshParts(EntityUid uid, UpgradeBatteryComponent component, RefreshPartsEvent args) + { + var powerCellRating = args.PartRatings[component.MachinePartPowerCapacity]; + + if (TryComp(uid, out var batteryComp)) + { + _batterySystem.SetMaxCharge(uid, MathF.Pow(component.MaxChargeMultiplier, powerCellRating - 1) * component.BaseMaxCharge, batteryComp); + } + } + + private void OnUpgradeExamine(EntityUid uid, UpgradeBatteryComponent component, UpgradeExamineEvent args) + { + // UpgradeBatteryComponent.MaxChargeMultiplier is not the actual multiplier, so we have to do this. + if (TryComp(uid, out var batteryComp)) + { + args.AddPercentageUpgrade("upgrade-max-charge", batteryComp.MaxCharge / component.BaseMaxCharge); + } + } + } +} diff --git a/Content.Server/Power/EntitySystems/UpgradePowerSystem.cs b/Content.Server/Power/EntitySystems/UpgradePowerSystem.cs new file mode 100644 index 00000000000..d2f6ee4f568 --- /dev/null +++ b/Content.Server/Power/EntitySystems/UpgradePowerSystem.cs @@ -0,0 +1,151 @@ +using Content.Server.Construction; +using Content.Server.Construction.Components; +using Content.Server.Power.Components; + +namespace Content.Server.Power.EntitySystems; + +/// +/// This handles using upgraded machine parts +/// to modify the power supply/generation of a machine. +/// +public sealed class UpgradePowerSystem : EntitySystem +{ + /// + public override void Initialize() + { + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); + + SubscribeLocalEvent(OnSupplierMapInit); + SubscribeLocalEvent(OnSupplierRefreshParts); + SubscribeLocalEvent(OnSupplierUpgradeExamine); + + SubscribeLocalEvent(OnSupplyRampingMapInit); + SubscribeLocalEvent(OnSupplyRampingRefreshParts); + SubscribeLocalEvent(OnSupplyRampingUpgradeExamine); + } + + private void OnMapInit(EntityUid uid, UpgradePowerDrawComponent component, MapInitEvent args) + { + if (TryComp(uid, out var powa)) + component.BaseLoad = powa.DrawRate; + else if (TryComp(uid, out var powa2)) + component.BaseLoad = powa2.Load; + } + + private void OnRefreshParts(EntityUid uid, UpgradePowerDrawComponent component, RefreshPartsEvent args) + { + var load = component.BaseLoad; + var rating = args.PartRatings[component.MachinePartPowerDraw]; + switch (component.Scaling) + + { + case MachineUpgradeScalingType.Linear: + load += component.PowerDrawMultiplier * (rating - 1); + break; + case MachineUpgradeScalingType.Exponential: + load *= MathF.Pow(component.PowerDrawMultiplier, rating - 1); + break; + default: + Log.Error($"invalid power scaling type for {ToPrettyString(uid)}."); + load = 0; + break; + } + + if (TryComp(uid, out var powa)) + powa.Load = load; + if (TryComp(uid, out var powa2)) + powa2.DrawRate = load; + } + + private void OnUpgradeExamine(EntityUid uid, UpgradePowerDrawComponent component, UpgradeExamineEvent args) + { + // UpgradePowerDrawComponent.PowerDrawMultiplier is not the actual multiplier, so we have to do this. + var powerDrawMultiplier = CompOrNull(uid)?.Load / component.BaseLoad + ?? CompOrNull(uid)?.DrawRate / component.BaseLoad; + + if (powerDrawMultiplier is not null) + args.AddPercentageUpgrade("upgrade-power-draw", powerDrawMultiplier.Value); + } + + private void OnSupplierMapInit(EntityUid uid, UpgradePowerSupplierComponent component, MapInitEvent args) + { + if (!TryComp(uid, out var supplier)) + return; + + component.BaseSupplyRate = supplier.MaxSupply; + } + + private void OnSupplierRefreshParts(EntityUid uid, UpgradePowerSupplierComponent component, RefreshPartsEvent args) + { + var supply = component.BaseSupplyRate; + var rating = args.PartRatings[component.MachinePartPowerSupply]; + switch (component.Scaling) + + { + case MachineUpgradeScalingType.Linear: + supply += component.PowerSupplyMultiplier * component.BaseSupplyRate * (rating - 1); + break; + case MachineUpgradeScalingType.Exponential: + supply *= MathF.Pow(component.PowerSupplyMultiplier, rating - 1); + break; + default: + Log.Error($"invalid power scaling type for {ToPrettyString(uid)}."); + supply = component.BaseSupplyRate; + break; + } + + component.ActualScalar = supply / component.BaseSupplyRate; + + if (!TryComp(uid, out var powa)) + return; + + powa.MaxSupply = supply; + } + + private void OnSupplierUpgradeExamine(EntityUid uid, UpgradePowerSupplierComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("upgrade-power-supply", component.ActualScalar); + } + + private void OnSupplyRampingMapInit(EntityUid uid, UpgradePowerSupplyRampingComponent component, MapInitEvent args) + { + if (!TryComp(uid, out var battery)) + return; + + component.BaseRampRate = battery.SupplyRampRate; + } + + private void OnSupplyRampingRefreshParts(EntityUid uid, UpgradePowerSupplyRampingComponent component, RefreshPartsEvent args) + { + var rampRate = component.BaseRampRate; + var rating = args.PartRatings[component.MachinePartRampRate]; + switch (component.Scaling) + + { + case MachineUpgradeScalingType.Linear: + rampRate += component.SupplyRampingMultiplier * component.BaseRampRate * (rating - 1); + break; + case MachineUpgradeScalingType.Exponential: + rampRate *= MathF.Pow(component.SupplyRampingMultiplier, rating - 1); + break; + default: + Log.Error($"invalid power supply ramping type for {ToPrettyString(uid)}."); + rampRate = component.BaseRampRate; + break; + } + + component.ActualScalar = rampRate / component.BaseRampRate; + + if (!TryComp(uid, out var battery)) + return; + + battery.SupplyRampRate = rampRate; + } + + private void OnSupplyRampingUpgradeExamine(EntityUid uid, UpgradePowerSupplyRampingComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("upgrade-power-supply-ramping", component.ActualScalar); + } +} diff --git a/Content.Server/Power/Generator/GeneratorSystem.cs b/Content.Server/Power/Generator/GeneratorSystem.cs index a75d1e4113d..721a959820b 100644 --- a/Content.Server/Power/Generator/GeneratorSystem.cs +++ b/Content.Server/Power/Generator/GeneratorSystem.cs @@ -26,8 +26,11 @@ public sealed class GeneratorSystem : SharedGeneratorSystem [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PuddleSystem _puddle = default!; + private EntityQuery _upgradeQuery; + public override void Initialize() { + _upgradeQuery = GetEntityQuery(); UpdatesBefore.Add(typeof(PowerNetSystem)); @@ -225,7 +228,9 @@ public override void Update(float frameTime) supplier.Enabled = true; - supplier.MaxSupply = gen.TargetPower; + var upgradeMultiplier = _upgradeQuery.CompOrNull(uid)?.ActualScalar ?? 1f; + + supplier.MaxSupply = gen.TargetPower * upgradeMultiplier; var eff = 1 / CalcFuelEfficiency(gen.TargetPower, gen.OptimalPower, gen); var consumption = gen.OptimalBurnRate * frameTime * eff; diff --git a/Content.Server/Shuttles/Components/ThrusterComponent.cs b/Content.Server/Shuttles/Components/ThrusterComponent.cs index 3bba9b5a7f0..f64d9bdcbf0 100644 --- a/Content.Server/Shuttles/Components/ThrusterComponent.cs +++ b/Content.Server/Shuttles/Components/ThrusterComponent.cs @@ -1,8 +1,10 @@ using System.Numerics; using Content.Server.Shuttles.Systems; +using Content.Shared.Construction.Prototypes; using Content.Shared.Damage; using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Shuttles.Components { @@ -13,7 +15,7 @@ public sealed partial class ThrusterComponent : Component /// /// Whether the thruster has been force to be enabled / disabled (e.g. VV, interaction, etc.) /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public bool Enabled { get; set; } = true; /// @@ -22,9 +24,12 @@ public sealed partial class ThrusterComponent : Component public bool IsOn; // Need to serialize this because RefreshParts isn't called on Init and this will break post-mapinit maps! - [ViewVariables(VVAccess.ReadWrite), DataField("thrust")] + [DataField] public float Thrust = 100f; + [DataField] + public float BaseThrust = 100f; + [DataField("thrusterType")] public ThrusterType Type = ThrusterType.Linear; @@ -37,24 +42,31 @@ public sealed partial class ThrusterComponent : Component }; /// - /// How much damage is done per second to anything colliding with our thrust. + /// How much damage is done per second to anything colliding with our thrust. /// - [DataField("damage")] public DamageSpecifier? Damage = new(); + [DataField] + public DamageSpecifier? Damage = new(); - [DataField("requireSpace")] + [DataField] public bool RequireSpace = true; // Used for burns public List Colliding = new(); - public bool Firing = false; + public bool Firing; /// - /// Next time we tick damage for anyone colliding. + /// Next time we tick damage for anyone colliding. /// - [ViewVariables(VVAccess.ReadWrite), DataField("nextFire", customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] public TimeSpan NextFire; + + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartThrust = "Capacitor"; + + [DataField] + public float PartRatingThrustMultiplier = 1.5f; } public enum ThrusterType diff --git a/Content.Server/Shuttles/Systems/ThrusterSystem.cs b/Content.Server/Shuttles/Systems/ThrusterSystem.cs index be55cd9a62a..ee131d7857a 100644 --- a/Content.Server/Shuttles/Systems/ThrusterSystem.cs +++ b/Content.Server/Shuttles/Systems/ThrusterSystem.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Server.Audio; +using Content.Server.Construction; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Shuttles.Components; @@ -51,6 +52,9 @@ public override void Initialize() SubscribeLocalEvent(OnThrusterExamine); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); + SubscribeLocalEvent(OnShuttleTileChange); } @@ -580,6 +584,24 @@ public void SetAngularThrust(ShuttleComponent component, bool on) } } + private void OnRefreshParts(EntityUid uid, ThrusterComponent component, RefreshPartsEvent args) + { + if (component.IsOn) // safely disable thruster to prevent negative thrust + DisableThruster(uid, component); + + var thrustRating = args.PartRatings[component.MachinePartThrust]; + + component.Thrust = component.BaseThrust * MathF.Pow(component.PartRatingThrustMultiplier, thrustRating - 1); + + if (component.Enabled && CanEnable(uid, component)) + EnableThruster(uid, component); + } + + private void OnUpgradeExamine(EntityUid uid, ThrusterComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("thruster-comp-upgrade-thrust", component.Thrust / component.BaseThrust); + } + #endregion private int GetFlagIndex(DirectionFlag flag) diff --git a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs index a9763b64d90..652ca236e44 100644 --- a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs @@ -1,6 +1,7 @@ using System.Numerics; using System.Threading; using Content.Server.Administration.Logs; +using Content.Server.Construction; using Content.Server.DeviceLinking.Events; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -47,6 +48,8 @@ public override void Initialize() SubscribeLocalEvent(OnInteractHand); SubscribeLocalEvent>(OnGetVerb); SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnAnchorStateChanged); SubscribeLocalEvent(OnSignalReceived); } @@ -176,6 +179,20 @@ private void OnApcChanged(EntityUid uid, EmitterComponent component, ref PowerCh } } + private void OnRefreshParts(EntityUid uid, EmitterComponent component, RefreshPartsEvent args) + { + var fireRateRating = args.PartRatings[component.MachinePartFireRate]; + + component.FireInterval = component.BaseFireInterval * MathF.Pow(component.FireRateMultiplier, fireRateRating - 1); + component.FireBurstDelayMin = component.BaseFireBurstDelayMin * MathF.Pow(component.FireRateMultiplier, fireRateRating - 1); + component.FireBurstDelayMax = component.BaseFireBurstDelayMax * MathF.Pow(component.FireRateMultiplier, fireRateRating - 1); + } + + private void OnUpgradeExamine(EntityUid uid, EmitterComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("emitter-component-upgrade-fire-rate", (float) (component.BaseFireInterval.TotalSeconds / component.FireInterval.TotalSeconds)); + } + public void SwitchOff(EntityUid uid, EmitterComponent component) { component.IsOn = false; diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index 25f64ba4b64..7187a704768 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -265,6 +265,12 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi listing.PurchaseAmount++; //track how many times something has been purchased _audio.PlayEntity(component.BuySuccessSound, msg.Session, uid); //cha-ching! + if (listing.SaleLimit != 0 && listing.DiscountValue > 0 && listing.PurchaseAmount >= listing.SaleLimit) + { + listing.DiscountValue = 0; + listing.Cost = listing.OldCost; + } + UpdateUserInterface(buyer, uid, component); } diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index 72aeb29d195..a86dffef633 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -10,6 +10,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using System.Linq; +using Content.Server.StoreDiscount; using Robust.Shared.Utility; namespace Content.Server.Store.Systems; @@ -22,6 +23,7 @@ public sealed partial class StoreSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly StoreDiscountSystem _storeDiscount = default!; public override void Initialize() { @@ -199,6 +201,8 @@ public void InitializeFromPreset(StorePresetPrototype preset, EntityUid uid, Sto if (component.Balance == new Dictionary() && preset.InitialBalance != null) //if we don't have a value stored, use the preset TryAddCurrency(preset.InitialBalance, uid, component); + _storeDiscount.ApplyDiscounts(component.Listings, preset); + var ui = _ui.GetUiOrNull(uid, StoreUiKey.Key); if (ui != null) { @@ -225,7 +229,7 @@ public CurrencyInsertAttemptEvent(EntityUid user, EntityUid target, EntityUid us /// -/// Nyano/DeltaV Code. For penguin bombs and what not. +/// Nyano/DeltaV Code. For penguin bombs and what not. /// Raised on an item when it is purchased. /// An item may need to set it upself up for its purchaser. /// For example, to make sure it isn't hostile to them or diff --git a/Content.Server/StoreDiscount/StoreDiscountSystem.cs b/Content.Server/StoreDiscount/StoreDiscountSystem.cs new file mode 100644 index 00000000000..8f77eba7801 --- /dev/null +++ b/Content.Server/StoreDiscount/StoreDiscountSystem.cs @@ -0,0 +1,55 @@ +using System.Linq; +using Content.Shared.FixedPoint; +using Content.Shared.Store; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.StoreDiscount; + +public sealed class StoreDiscountSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + public void ApplyDiscounts(IEnumerable listings, StorePresetPrototype store) + { + if (!store.Sales.Enabled) + return; + + var count = _random.Next(store.Sales.MinItems, store.Sales.MaxItems + 1); + + listings = listings + .Where(l => + !l.SaleBlacklist + && l.Cost.Any(x => x.Value > 1) + && store.Categories.Overlaps(ChangedFormatCategories(l.Categories))) + .OrderBy(_ => _random.Next()).Take(count).ToList(); + + foreach (var listing in listings) + { + var sale = GetDiscount(store.Sales.MinMultiplier, store.Sales.MaxMultiplier); + var newCost = listing.Cost.ToDictionary(x => x.Key, + x => FixedPoint2.New(Math.Max(1, (int) MathF.Round(x.Value.Float() * sale)))); + + if (listing.Cost.All(x => x.Value.Int() == newCost[x.Key].Int())) + continue; + + var key = listing.Cost.First(x => x.Value > 0).Key; + listing.OldCost = listing.Cost; + listing.DiscountValue = 100 - (newCost[key] / listing.Cost[key] * 100).Int(); + listing.Cost = newCost; + listing.Categories = new() {store.Sales.SalesCategory}; + } + } + + private IEnumerable ChangedFormatCategories(List> categories) + { + var modified = from p in categories select p.Id; + + return modified; + } + + private float GetDiscount(float minMultiplier, float maxMultiplier) + { + return _random.NextFloat() * (maxMultiplier - minMultiplier) + minMultiplier; + } +} diff --git a/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs b/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs index 756f44e7429..a35972f1c86 100644 --- a/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs +++ b/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs @@ -13,7 +13,7 @@ public sealed partial class ForeignerTraitComponent : Component { /// /// The "base" language that is to be removed and substituted with a translator. - /// By default, equals to the fallback language, which is GalacticCommon. + /// By default, equals to the fallback language, which is TauCetiBasic. /// [DataField] public ProtoId BaseLanguage = SharedLanguageSystem.FallbackLanguagePrototype; diff --git a/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs b/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs index 58e974227ce..2c7274a13d5 100644 --- a/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs +++ b/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs @@ -46,7 +46,7 @@ private void OnSpawn(Entity entity, ref ComponentInit a if (TryGiveTranslator(entity.Owner, entity.Comp.BaseTranslator, entity.Comp.BaseLanguage, alternateLanguage, out var translator)) { - _languages.RemoveLanguage(entity, entity.Comp.BaseLanguage, entity.Comp.CantSpeak, entity.Comp.CantUnderstand, knowledge); + _languages.RemoveLanguage(entity, entity.Comp.BaseLanguage, entity.Comp.CantSpeak, entity.Comp.CantUnderstand); } } diff --git a/Content.Server/Traits/Assorted/LanguageKnowledgeModifierComponent.cs b/Content.Server/Traits/Assorted/LanguageKnowledgeModifierComponent.cs deleted file mode 100644 index 170dae40fa6..00000000000 --- a/Content.Server/Traits/Assorted/LanguageKnowledgeModifierComponent.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Content.Shared.Language; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Server.Traits.Assorted; - -/// -/// Used for traits that modify entities' language knowledge. -/// -[RegisterComponent] -public sealed partial class LanguageKnowledgeModifierComponent : Component -{ - /// - /// List of languages this entity will learn to speak. - /// - [DataField("speaks")] - public List NewSpokenLanguages = new(); - - /// - /// List of languages this entity will learn to understand. - /// - [DataField("understands")] - public List NewUnderstoodLanguages = new(); -} diff --git a/Content.Server/Traits/Assorted/LanguageKnowledgeModifierSystem.cs b/Content.Server/Traits/Assorted/LanguageKnowledgeModifierSystem.cs deleted file mode 100644 index 9053c9404fe..00000000000 --- a/Content.Server/Traits/Assorted/LanguageKnowledgeModifierSystem.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Linq; -using Content.Server.Language; -using Content.Shared.Language.Components; - -namespace Content.Server.Traits.Assorted; - -public sealed class LanguageKnowledgeModifierSystem : EntitySystem -{ - [Dependency] private readonly LanguageSystem _languages = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnStartup); - } - - private void OnStartup(Entity entity, ref ComponentInit args) - { - if (!TryComp(entity, out var knowledge)) - { - Log.Warning($"Entity {entity.Owner} does not have a LanguageKnowledge but has a LanguageKnowledgeModifier!"); - return; - } - - foreach (var spokenLanguage in entity.Comp.NewSpokenLanguages) - { - _languages.AddLanguage(entity, spokenLanguage, true, false, knowledge); - } - - foreach (var understoodLanguage in entity.Comp.NewUnderstoodLanguages) - { - _languages.AddLanguage(entity, understoodLanguage, false, true, knowledge); - } - } -} diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs index 39812f65b6d..bd36b4ecefb 100644 --- a/Content.Server/Traits/TraitSystem.cs +++ b/Content.Server/Traits/TraitSystem.cs @@ -12,6 +12,7 @@ using Robust.Shared.Utility; using Content.Server.Abilities.Psionics; using Content.Shared.Psionics; +using Content.Server.Language; using Content.Shared.Mood; namespace Content.Server.Traits; @@ -26,6 +27,7 @@ public sealed class TraitSystem : EntitySystem [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly PsionicAbilitiesSystem _psionicAbilities = default!; [Dependency] private readonly IComponentFactory _componentFactory = default!; + [Dependency] private readonly LanguageSystem _languageSystem = default!; public override void Initialize() { @@ -66,6 +68,8 @@ public void AddTrait(EntityUid uid, TraitPrototype traitPrototype) AddTraitComponents(uid, traitPrototype); AddTraitActions(uid, traitPrototype); AddTraitPsionics(uid, traitPrototype); + AddTraitLanguage(uid, traitPrototype); + RemoveTraitLanguage(uid, traitPrototype); AddTraitMoodlets(uid, traitPrototype); } @@ -142,6 +146,72 @@ public void AddTraitPsionics(EntityUid uid, TraitPrototype traitPrototype) _psionicAbilities.InitializePsionicPower(uid, psionicPower, false); } + /// + /// Initialize languages given by a Trait. + /// + private void AddTraitLanguage(EntityUid uid, TraitPrototype traitPrototype) + { + AddTraitLanguagesSpoken(uid, traitPrototype); + AddTraitLanguagesUnderstood(uid, traitPrototype); + } + + /// + /// If a trait includes any Spoken Languages, this sends them to LanguageSystem to be initialized. + /// + public void AddTraitLanguagesSpoken(EntityUid uid, TraitPrototype traitPrototype) + { + if (traitPrototype.LanguagesSpoken is null) + return; + + foreach (var language in traitPrototype.LanguagesSpoken) + _languageSystem.AddLanguage(uid, language, true, false); + } + + /// + /// If a trait includes any Understood Languages, this sends them to LanguageSystem to be initialized. + /// + public void AddTraitLanguagesUnderstood(EntityUid uid, TraitPrototype traitPrototype) + { + if (traitPrototype.LanguagesUnderstood is null) + return; + + foreach (var language in traitPrototype.LanguagesUnderstood) + _languageSystem.AddLanguage(uid, language, false, true); + } + + /// + /// Remove Languages given by a Trait. + /// + private void RemoveTraitLanguage(EntityUid uid, TraitPrototype traitPrototype) + { + RemoveTraitLanguagesSpoken(uid, traitPrototype); + RemoveTraitLanguagesUnderstood(uid, traitPrototype); + } + + /// + /// Removes any Spoken Languages if defined by a trait. + /// + public void RemoveTraitLanguagesSpoken(EntityUid uid, TraitPrototype traitPrototype) + { + if (traitPrototype.RemoveLanguagesSpoken is null) + return; + + foreach (var language in traitPrototype.RemoveLanguagesSpoken) + _languageSystem.RemoveLanguage(uid, language, true, false); + } + + /// + /// Removes any Understood Languages if defined by a trait. + /// + public void RemoveTraitLanguagesUnderstood(EntityUid uid, TraitPrototype traitPrototype) + { + if (traitPrototype.RemoveLanguagesUnderstood is null) + return; + + foreach (var language in traitPrototype.RemoveLanguagesUnderstood) + _languageSystem.RemoveLanguage(uid, language, false, true); + } + /// /// If a trait includes any moodlets, this adds the moodlets to the receiving entity. /// While I can't stop you, you shouldn't use this to add temporary moodlets. diff --git a/Content.Server/Xenoarchaeology/Equipment/Components/TraversalDistorterComponent.cs b/Content.Server/Xenoarchaeology/Equipment/Components/TraversalDistorterComponent.cs index ec16083c538..d6a39fe4f40 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Components/TraversalDistorterComponent.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Components/TraversalDistorterComponent.cs @@ -1,4 +1,7 @@ -namespace Content.Server.Xenoarchaeology.Equipment.Components; +using Content.Shared.Construction.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Xenoarchaeology.Equipment.Components; /// /// This is used for a machine that biases @@ -7,6 +10,18 @@ [RegisterComponent] public sealed partial class TraversalDistorterComponent : Component { + [ViewVariables(VVAccess.ReadWrite)] + public float BiasChance; + + [DataField] + public float BaseBiasChance = 0.7f; + + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartBiasChance = "Manipulator"; + + [DataField] + public float PartRatingBiasChance = 1.1f; + [ViewVariables(VVAccess.ReadWrite)] public BiasDirection BiasDirection = BiasDirection.In; diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs index 230e639af49..bb662925a92 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs @@ -1,9 +1,12 @@ -using Content.Server.Popups; +using Content.Server.Construction; +using Content.Server.Popups; using Content.Server.Power.EntitySystems; using Content.Server.Xenoarchaeology.Equipment.Components; +using Content.Server.Xenoarchaeology.XenoArtifacts; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Placeable; +using Robust.Shared.Player; using Robust.Shared.Timing; namespace Content.Server.Xenoarchaeology.Equipment.Systems; @@ -20,6 +23,8 @@ public override void Initialize() SubscribeLocalEvent(OnInteract); SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); SubscribeLocalEvent(OnItemPlaced); SubscribeLocalEvent(OnItemRemoved); @@ -68,10 +73,21 @@ private void OnExamine(EntityUid uid, TraversalDistorterComponent component, Exa examine = Loc.GetString("traversal-distorter-desc-out"); break; } - args.PushMarkup(examine); } + private void OnRefreshParts(EntityUid uid, TraversalDistorterComponent component, RefreshPartsEvent args) + { + var biasRating = args.PartRatings[component.MachinePartBiasChance]; + + component.BiasChance = component.BaseBiasChance * MathF.Pow(component.PartRatingBiasChance, biasRating - 1); + } + + private void OnUpgradeExamine(EntityUid uid, TraversalDistorterComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("traversal-distorter-upgrade-bias", component.BiasChance / component.BaseBiasChance); + } + private void OnItemPlaced(EntityUid uid, TraversalDistorterComponent component, ref ItemPlacedEvent args) { var bias = EnsureComp(args.OtherEntity); diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.cs index 955fe827d72..a7948aa7ff9 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/ArtifactSystem.cs @@ -206,6 +206,7 @@ public void ForceActivateArtifact(EntityUid uid, EntityUid? user = null, Artifac if (TryComp(uid, out var bias) && TryComp(bias.Provider, out var trav) && + _random.Prob(trav.BiasChance) && this.IsPowered(bias.Provider, EntityManager)) { switch (trav.BiasDirection) diff --git a/Content.Shared/Actions/Events/PsionicHealOtherPowerActionEvent.cs b/Content.Shared/Actions/Events/PsionicHealOtherPowerActionEvent.cs new file mode 100644 index 00000000000..8cf11b9e66d --- /dev/null +++ b/Content.Shared/Actions/Events/PsionicHealOtherPowerActionEvent.cs @@ -0,0 +1,59 @@ +using Robust.Shared.Audio; +using Content.Shared.Damage; +using Content.Shared.Popups; + +namespace Content.Shared.Actions.Events; +public sealed partial class PsionicHealOtherPowerActionEvent : EntityTargetActionEvent +{ + [DataField] + public DamageSpecifier HealingAmount = default!; + + [DataField] + public string PowerName; + + /// Controls whether or not a power fires immediately and with no DoAfter + [DataField] + public bool Immediate; + + [DataField] + public string? PopupText; + + [DataField] + public float RotReduction; + + [DataField] + public bool DoRevive; + + [DataField] + public bool BreakOnUserMove = true; + + [DataField] + public bool BreakOnTargetMove = false; + + [DataField] + public float UseDelay = 8f; + + [DataField] + public int MinGlimmer = 8; + + [DataField] + public int MaxGlimmer = 12; + + [DataField] + public int GlimmerObviousSoundThreshold; + + [DataField] + public int GlimmerObviousPopupThreshold; + + [DataField] + public PopupType PopupType = PopupType.Medium; + + [DataField] + public AudioParams AudioParams = default!; + + [DataField] + public bool PlaySound; + + [DataField] + public SoundSpecifier SoundUse = new SoundPathSpecifier("/Audio/Psionics/heartbeat_fast.ogg"); +} diff --git a/Content.Shared/Atmos/Consoles/Components/AtmosAlertsComputerComponent.cs b/Content.Shared/Atmos/Consoles/Components/AtmosAlertsComputerComponent.cs new file mode 100644 index 00000000000..d64c8907afb --- /dev/null +++ b/Content.Shared/Atmos/Consoles/Components/AtmosAlertsComputerComponent.cs @@ -0,0 +1,235 @@ +using Content.Shared.Atmos.Consoles; +using Content.Shared.Atmos.Monitor; +using Robust.Shared.GameStates; +using Robust.Shared.Map; +using Robust.Shared.Serialization; + +namespace Content.Shared.Atmos.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedAtmosAlertsComputerSystem))] +public sealed partial class AtmosAlertsComputerComponent : Component +{ + /// + /// The current entity of interest (selected via the console UI) + /// + [ViewVariables] + public NetEntity? FocusDevice; + + /// + /// A list of all the atmos devices that will be used to populate the nav map + /// + [ViewVariables, AutoNetworkedField] + public HashSet AtmosDevices = new(); + + /// + /// A list of all the air alarms that have had their alerts silenced on this particular console + /// + [ViewVariables, AutoNetworkedField] + public HashSet SilencedDevices = new(); +} + +[Serializable, NetSerializable] +public struct AtmosAlertsDeviceNavMapData +{ + /// + /// The entity in question + /// + public NetEntity NetEntity; + + /// + /// Location of the entity + /// + public NetCoordinates NetCoordinates; + + /// + /// Used to determine what map icons to use + /// + public AtmosAlertsComputerGroup Group; + + /// + /// Populate the atmos monitoring console nav map with a single entity + /// + public AtmosAlertsDeviceNavMapData(NetEntity netEntity, NetCoordinates netCoordinates, AtmosAlertsComputerGroup group) + { + NetEntity = netEntity; + NetCoordinates = netCoordinates; + Group = group; + } +} + +[Serializable, NetSerializable] +public struct AtmosAlertsFocusDeviceData +{ + /// + /// Focus entity + /// + public NetEntity NetEntity; + + /// + /// Temperature (K) and related alert state + /// + public (float, AtmosAlarmType) TemperatureData; + + /// + /// Pressure (kPA) and related alert state + /// + public (float, AtmosAlarmType) PressureData; + + /// + /// Moles, percentage, and related alert state, for all detected gases + /// + public Dictionary GasData; + + /// + /// Populates the atmos monitoring console focus entry with atmospheric data + /// + public AtmosAlertsFocusDeviceData + (NetEntity netEntity, + (float, AtmosAlarmType) temperatureData, + (float, AtmosAlarmType) pressureData, + Dictionary gasData) + { + NetEntity = netEntity; + TemperatureData = temperatureData; + PressureData = pressureData; + GasData = gasData; + } +} + +[Serializable, NetSerializable] +public sealed class AtmosAlertsComputerBoundInterfaceState : BoundUserInterfaceState +{ + /// + /// A list of all air alarms + /// + public AtmosAlertsComputerEntry[] AirAlarms; + + /// + /// A list of all fire alarms + /// + public AtmosAlertsComputerEntry[] FireAlarms; + + /// + /// Data for the UI focus (if applicable) + /// + public AtmosAlertsFocusDeviceData? FocusData; + + /// + /// Sends data from the server to the client to populate the atmos monitoring console UI + /// + public AtmosAlertsComputerBoundInterfaceState(AtmosAlertsComputerEntry[] airAlarms, AtmosAlertsComputerEntry[] fireAlarms, AtmosAlertsFocusDeviceData? focusData) + { + AirAlarms = airAlarms; + FireAlarms = fireAlarms; + FocusData = focusData; + } +} + +[Serializable, NetSerializable] +public struct AtmosAlertsComputerEntry +{ + /// + /// The entity in question + /// + public NetEntity NetEntity; + + /// + /// Location of the entity + /// + public NetCoordinates Coordinates; + + /// + /// The type of entity + /// + public AtmosAlertsComputerGroup Group; + + /// + /// Current alarm state + /// + public AtmosAlarmType AlarmState; + + /// + /// Localised device name + /// + public string EntityName; + + /// + /// Device network address + /// + public string Address; + + /// + /// Used to populate the atmos monitoring console UI with data from a single air alarm + /// + public AtmosAlertsComputerEntry + (NetEntity entity, + NetCoordinates coordinates, + AtmosAlertsComputerGroup group, + AtmosAlarmType alarmState, + string entityName, + string address) + { + NetEntity = entity; + Coordinates = coordinates; + Group = group; + AlarmState = alarmState; + EntityName = entityName; + Address = address; + } +} + +[Serializable, NetSerializable] +public sealed class AtmosAlertsComputerFocusChangeMessage : BoundUserInterfaceMessage +{ + public NetEntity? FocusDevice; + + /// + /// Used to inform the server that the specified focus for the atmos monitoring console has been changed by the client + /// + public AtmosAlertsComputerFocusChangeMessage(NetEntity? focusDevice) + { + FocusDevice = focusDevice; + } +} + +[Serializable, NetSerializable] +public sealed class AtmosAlertsComputerDeviceSilencedMessage : BoundUserInterfaceMessage +{ + public NetEntity AtmosDevice; + public bool SilenceDevice = true; + + /// + /// Used to inform the server that the client has silenced alerts from the specified device to this atmos monitoring console + /// + public AtmosAlertsComputerDeviceSilencedMessage(NetEntity atmosDevice, bool silenceDevice = true) + { + AtmosDevice = atmosDevice; + SilenceDevice = silenceDevice; + } +} + +/// +/// List of all the different atmos device groups +/// +public enum AtmosAlertsComputerGroup +{ + Invalid, + AirAlarm, + FireAlarm, +} + +[NetSerializable, Serializable] +public enum AtmosAlertsComputerVisuals +{ + ComputerLayerScreen, +} + +/// +/// UI key associated with the atmos monitoring console +/// +[Serializable, NetSerializable] +public enum AtmosAlertsComputerUiKey +{ + Key +} diff --git a/Content.Shared/Atmos/Consoles/Components/AtmosAlertsDeviceComponent.cs b/Content.Shared/Atmos/Consoles/Components/AtmosAlertsDeviceComponent.cs new file mode 100644 index 00000000000..881d60b084c --- /dev/null +++ b/Content.Shared/Atmos/Consoles/Components/AtmosAlertsDeviceComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Atmos.Components; + +[RegisterComponent, NetworkedComponent] +[Access([])] +public sealed partial class AtmosAlertsDeviceComponent : Component +{ + /// + /// The group that the entity belongs to + /// + [DataField, ViewVariables] + public AtmosAlertsComputerGroup Group; +} diff --git a/Content.Shared/Atmos/Consoles/SharedAtmosAlertsComputerSystem.cs b/Content.Shared/Atmos/Consoles/SharedAtmosAlertsComputerSystem.cs new file mode 100644 index 00000000000..7e2b2b04670 --- /dev/null +++ b/Content.Shared/Atmos/Consoles/SharedAtmosAlertsComputerSystem.cs @@ -0,0 +1,24 @@ +using Content.Shared.Atmos.Components; + +namespace Content.Shared.Atmos.Consoles; + +public abstract partial class SharedAtmosAlertsComputerSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDeviceSilencedMessage); + } + + private void OnDeviceSilencedMessage(EntityUid uid, AtmosAlertsComputerComponent component, AtmosAlertsComputerDeviceSilencedMessage args) + { + if (args.SilenceDevice) + component.SilencedDevices.Add(args.AtmosDevice); + + else + component.SilencedDevices.Remove(args.AtmosDevice); + + Dirty(uid, component); + } +} diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 417817b5419..1b3bef5e333 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -2470,9 +2470,16 @@ public static readonly CVarDef public static readonly CVarDef HoldLookUp = CVarDef.Create("rest.hold_look_up", false, CVar.CLIENT | CVar.ARCHIVE); - + + /// + /// When true, entities that fall to the ground will be able to crawl under tables and + /// plastic flaps, allowing them to take cover from gunshots. + /// + public static readonly CVarDef CrawlUnderTables = + CVarDef.Create("rest.crawlundertables", false, CVar.REPLICATED); + #endregion - + #region Material Reclaimer /// @@ -2498,5 +2505,6 @@ public static readonly CVarDef CVarDef.Create("jetpack.enable_in_no_gravity", true, CVar.REPLICATED); #endregion + } } diff --git a/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs b/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs index 911ea41cca5..7c8a3625227 100644 --- a/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs +++ b/Content.Shared/Cargo/Components/SharedCargoTelepadComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Construction.Prototypes; using Content.Shared.DeviceLinking; using Robust.Shared.Audio; using Robust.Shared.GameStates; @@ -13,29 +14,47 @@ namespace Content.Shared.Cargo.Components; public sealed partial class CargoTelepadComponent : Component { /// - /// The actual amount of time it takes to teleport from the telepad + /// The base amount of time it takes to teleport from the telepad /// - [DataField("delay"), ViewVariables(VVAccess.ReadWrite)] + [DataField] + public float BaseDelay = 10f; + + /// + /// The actual amount of time it takes to teleport from the telepad + /// + [DataField] public float Delay = 10f; /// - /// How much time we've accumulated until next teleport. + /// The machine part that affects + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] + public string MachinePartTeleportDelay = "Capacitor"; + + /// + /// A multiplier applied to for each level of + /// + [DataField] + public float PartRatingTeleportDelay = 0.8f; + + /// + /// How much time we've accumulated until next teleport. /// - [DataField("accumulator"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float Accumulator; - [DataField("currentState")] + [DataField] public CargoTelepadState CurrentState = CargoTelepadState.Unpowered; - [DataField("teleportSound")] + [DataField] public SoundSpecifier TeleportSound = new SoundPathSpecifier("/Audio/Machines/phasein.ogg"); /// /// The paper-type prototype to spawn with the order information. /// - [DataField("printerOutput", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] public string PrinterOutput = "PaperCargoInvoice"; - [DataField("receiverPort", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] public string ReceiverPort = "OrderReceiver"; } diff --git a/Content.Shared/Cloning/CloningPodComponent.cs b/Content.Shared/Cloning/CloningPodComponent.cs index 082b92e8b14..c9a6fd4500b 100644 --- a/Content.Shared/Cloning/CloningPodComponent.cs +++ b/Content.Shared/Cloning/CloningPodComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Construction.Prototypes; using Content.Shared.DeviceLinking; using Content.Shared.Materials; using Content.Shared.Random; @@ -38,6 +39,18 @@ public sealed partial class CloningPodComponent : Component [DataField] public ProtoId RequiredMaterial = "Biomass"; + /// + /// The multiplier for cloning duration + /// + [DataField] + public float PartRatingSpeedMultiplier = 0.75f; + + /// + /// The machine part that affects cloning speed + /// + [DataField] + public ProtoId MachinePartCloningSpeed = "Manipulator"; + /// /// The current amount of time it takes to clone a body /// @@ -66,6 +79,18 @@ public sealed partial class CloningPodComponent : Component Params = AudioParams.Default.WithVolume(4), }; + /// + /// The machine part that affects how much biomass is needed to clone a body. + /// + [DataField] + public float PartRatingMaterialMultiplier = 0.85f; + + /// + /// The machine part that decreases the amount of material needed for cloning + /// + [DataField] + public ProtoId MachinePartMaterialUse = "MatterBin"; + [ViewVariables(VVAccess.ReadWrite)] public CloningPodStatus Status; diff --git a/Content.Shared/Construction/MachinePartSystem.cs b/Content.Shared/Construction/MachinePartSystem.cs index 1a19040b410..01db7fbade3 100644 --- a/Content.Shared/Construction/MachinePartSystem.cs +++ b/Content.Shared/Construction/MachinePartSystem.cs @@ -21,6 +21,7 @@ public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMachineBoardExamined); + SubscribeLocalEvent(OnMachinePartExamined); } private void OnMachineBoardExamined(EntityUid uid, MachineBoardComponent component, ExaminedEvent args) @@ -61,6 +62,20 @@ private void OnMachineBoardExamined(EntityUid uid, MachineBoardComponent compone } } + private void OnMachinePartExamined(EntityUid uid, MachinePartComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + using (args.PushGroup(nameof(MachinePartComponent))) + { + args.PushMarkup(Loc.GetString("machine-part-component-on-examine-rating-text", + ("rating", component.Rating))); + args.PushMarkup(Loc.GetString("machine-part-component-on-examine-type-text", ("type", + Loc.GetString(_prototype.Index(component.PartType).Name)))); + } + } + public Dictionary GetMachineBoardMaterialCost(Entity entity, int coefficient = 1) { var (_, comp) = entity; diff --git a/Content.Shared/Contests/ContestsSystem.Utilities.cs b/Content.Shared/Contests/ContestsSystem.Utilities.cs index 42a69bff478..0278d75e943 100644 --- a/Content.Shared/Contests/ContestsSystem.Utilities.cs +++ b/Content.Shared/Contests/ContestsSystem.Utilities.cs @@ -1,4 +1,5 @@ using Content.Shared.CCVar; +using Robust.Shared.Serialization; namespace Content.Shared.Contests; public sealed partial class ContestsSystem @@ -18,4 +19,265 @@ private bool ContestClampOverride(bool bypassClamp) { return _cfg.GetCVar(CCVars.AllowClampOverride) && bypassClamp; } + + /// + /// Constructor for feeding options from a given set of ContestArgs into the ContestsSystem. + /// Just multiply by this and give it a user EntityUid and a ContestArgs variable. That's all you need to know. + /// + public float ContestConstructor(EntityUid user, ContestArgs args) + { + if (!_cfg.GetCVar(CCVars.DoContestsSystem)) + return 1; + + if (!args.DoEveryInteraction) + return args.DoMassInteraction ? ((!args.MassDisadvantage + ? MassContest(user, args.MassBypassClamp, args.MassRangeModifier) + : 1 / MassContest(user, args.MassBypassClamp, args.MassRangeModifier)) + + args.MassOffset) + : 1 + * (args.DoStaminaInteraction ? ((!args.StaminaDisadvantage + ? StaminaContest(user, args.StaminaBypassClamp, args.StaminaRangeModifier) + : 1 / StaminaContest(user, args.StaminaBypassClamp, args.StaminaRangeModifier)) + + args.StaminaOffset) + : 1) + * (args.DoHealthInteraction ? ((!args.HealthDisadvantage + ? HealthContest(user, args.HealthBypassClamp, args.HealthRangeModifier) + : 1 / HealthContest(user, args.HealthBypassClamp, args.HealthRangeModifier)) + + args.HealthOffset) + : 1) + * (args.DoMindInteraction ? ((!args.MindDisadvantage + ? MindContest(user, args.MindBypassClamp, args.MindRangeModifier) + : 1 / MindContest(user, args.MindBypassClamp, args.MindRangeModifier)) + + args.MindOffset) + : 1) + * (args.DoMoodInteraction ? ((!args.MoodDisadvantage + ? MoodContest(user, args.MoodBypassClamp, args.MoodRangeModifier) + : 1 / MoodContest(user, args.MoodBypassClamp, args.MoodRangeModifier)) + + args.MoodOffset) + : 1); + + var everyContest = EveryContest(user, + args.MassBypassClamp, + args.StaminaBypassClamp, + args.HealthBypassClamp, + args.MindBypassClamp, + args.MoodBypassClamp, + args.MassRangeModifier, + args.StaminaRangeModifier, + args.HealthRangeModifier, + args.MindRangeModifier, + args.MoodRangeModifier, + args.EveryMassWeight, + args.EveryStaminaWeight, + args.EveryHealthWeight, + args.EveryMindWeight, + args.EveryMoodWeight, + args.EveryInteractionSumOrMultiply); + + return !args.EveryDisadvantage ? everyContest : 1 / everyContest; + } } + +[Serializable, NetSerializable, DataDefinition] +public sealed partial class ContestArgs +{ + /// + /// Controls whether this melee weapon allows for mass to factor into damage. + /// + [DataField] + public bool DoMassInteraction; + + /// + /// When true, mass provides a disadvantage. + /// + [DataField] + public bool MassDisadvantage; + + /// + /// When true, mass contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool MassBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mass contests for melee. + /// + [DataField] + public float MassRangeModifier = 1; + + /// + /// The output of a mass contest is increased by this amount. + /// + [DataField] + public float MassOffset; + + /// + /// Controls whether this melee weapon allows for stamina to factor into damage. + /// + [DataField] + public bool DoStaminaInteraction; + + /// + /// When true, stamina provides a disadvantage. + /// + [DataField] + public bool StaminaDisadvantage; + + /// + /// When true, stamina contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool StaminaBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mass contests for melee. + /// + [DataField] + public float StaminaRangeModifier = 1; + + /// + /// The output of a stamina contest is increased by this amount. + /// + [DataField] + public float StaminaOffset; + + /// + /// Controls whether this melee weapon allows for health to factor into damage. + /// + [DataField] + public bool DoHealthInteraction; + + /// + /// When true, health contests provide a disadvantage. + /// + [DataField] + public bool HealthDisadvantage; + + /// + /// When true, health contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool HealthBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mass contests for melee. + /// + [DataField] + public float HealthRangeModifier = 1; + + /// + /// The output of health contests is increased by this amount. + /// + [DataField] + public float HealthOffset; + + /// + /// Controls whether this melee weapon allows for psychic casting stats to factor into damage. + /// + [DataField] + public bool DoMindInteraction; + + /// + /// When true, high psychic casting stats provide a disadvantage. + /// + [DataField] + public bool MindDisadvantage; + + /// + /// When true, mind contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool MindBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mind contests for melee. + /// + [DataField] + public float MindRangeModifier = 1; + + /// + /// The output of a mind contest is increased by this amount. + /// + [DataField] + public float MindOffset; + + /// + /// Controls whether this melee weapon allows mood to factor into damage. + /// + [DataField] + public bool DoMoodInteraction; + + /// + /// When true, mood provides a disadvantage. + /// + [DataField] + public bool MoodDisadvantage; + + /// + /// When true, mood contests ignore clamp limitations for a melee weapon. + /// + [DataField] + public bool MoodBypassClamp; + + /// + /// Multiplies the acceptable range of outputs provided by mood contests for melee. + /// + [DataField] + public float MoodRangeModifier = 1; + + /// + /// The output of mood contests is increased by this amount. + /// + [DataField] + public float MoodOffset; + + /// + /// Enables the EveryContest interaction for a melee weapon. + /// IF YOU PUT THIS ON ANY WEAPON OTHER THAN AN ADMEME, I WILL COME TO YOUR HOUSE AND SEND YOU TO MEET YOUR CREATOR WHEN THE PLAYERS COMPLAIN. + /// + [DataField] + public bool DoEveryInteraction; + + /// + /// When true, EveryContest provides a disadvantage. + /// + [DataField] + public bool EveryDisadvantage; + + /// + /// How much Mass is considered for an EveryContest. + /// + [DataField] + public float EveryMassWeight = 1; + + /// + /// How much Stamina is considered for an EveryContest. + /// + [DataField] + public float EveryStaminaWeight = 1; + + /// + /// How much Health is considered for an EveryContest. + /// + [DataField] + public float EveryHealthWeight = 1; + + /// + /// How much psychic casting stats are considered for an EveryContest. + /// + [DataField] + public float EveryMindWeight = 1; + + /// + /// How much mood is considered for an EveryContest. + /// + [DataField] + public float EveryMoodWeight = 1; + + /// + /// When true, the EveryContest sums the results of all contests rather than multiplying them, + /// probably giving you a very, very, very large multiplier... + /// + [DataField] + public bool EveryInteractionSumOrMultiply; +} \ No newline at end of file diff --git a/Content.Shared/Language/Systems/SharedLanguageSystem.cs b/Content.Shared/Language/Systems/SharedLanguageSystem.cs index 0a03086ebe1..d9e882147c0 100644 --- a/Content.Shared/Language/Systems/SharedLanguageSystem.cs +++ b/Content.Shared/Language/Systems/SharedLanguageSystem.cs @@ -10,7 +10,7 @@ public abstract class SharedLanguageSystem : EntitySystem /// The language used as a fallback in cases where an entity suddenly becomes a language speaker (e.g. the usage of make-sentient) /// [ValidatePrototypeId] - public static readonly string FallbackLanguagePrototype = "GalacticCommon"; + public static readonly string FallbackLanguagePrototype = "TauCetiBasic"; /// /// The language whose speakers are assumed to understand and speak every language. Should never be added directly. diff --git a/Content.Shared/Materials/MaterialReclaimerComponent.cs b/Content.Shared/Materials/MaterialReclaimerComponent.cs index 3e72baf6041..2fd6cd6fc82 100644 --- a/Content.Shared/Materials/MaterialReclaimerComponent.cs +++ b/Content.Shared/Materials/MaterialReclaimerComponent.cs @@ -1,100 +1,121 @@ -using Content.Shared.Whitelist; +using Content.Shared.Construction.Prototypes; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Materials; /// -/// This is a machine that handles converting entities -/// into the raw materials and chemicals that make them up. +/// This is a machine that handles converting entities +/// into the raw materials and chemicals that make them up. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] [Access(typeof(SharedMaterialReclaimerSystem))] public sealed partial class MaterialReclaimerComponent : Component { /// - /// Whether or not the machine has power. We put it here - /// so we can network and predict it. + /// Whether or not the machine has power. We put it here + /// so we can network and predict it. /// - [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + [DataField, AutoNetworkedField] public bool Powered; /// - /// An "enable" toggle for things like interfacing with machine linking + /// An "enable" toggle for things like interfacing with machine linking /// - [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + [DataField, AutoNetworkedField] public bool Enabled = true; /// - /// How efficiently the materials are reclaimed. - /// In practice, a multiplier per material when calculating the output of the reclaimer. + /// How efficiently the materials are reclaimed. + /// In practice, a multiplier per material when calculating the output of the reclaimer. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float Efficiency = 1f; /// - /// Whether or not the process - /// speed scales with the amount of materials being processed - /// or if it's just + /// Whether or not the process + /// speed scales with the amount of materials being processed + /// or if it's just /// [DataField] public bool ScaleProcessSpeed = true; /// - /// How quickly it takes to consume X amount of materials per second. - /// For example, with a rate of 50, an entity with 100 total material takes 2 seconds to process. + /// How quickly it takes to consume X amount of materials per second. + /// For example, with a rate of 50, an entity with 100 total material takes 2 seconds to process. /// - [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] + [DataField] + public float BaseMaterialProcessRate = 100f; + + /// + /// How quickly it takes to consume X amount of materials per second. + /// For example, with a rate of 50, an entity with 100 total material takes 2 seconds to process. + /// + [DataField, AutoNetworkedField] public float MaterialProcessRate = 100f; /// - /// The minimum amount fo time it can take to process an entity. - /// this value supercedes the calculated one using + /// Machine part whose rating modifies + /// + [DataField] + public ProtoId MachinePartProcessRate = "Manipulator"; + + /// + /// How much the machine part quality affects the /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] + public float PartRatingProcessRateMultiplier = 1.5f; + + /// + /// The minimum amount fo time it can take to process an entity. + /// this value supercedes the calculated one using + /// + [DataField] public TimeSpan MinimumProcessDuration = TimeSpan.FromSeconds(0.5f); /// - /// The id of our output solution + /// The id of our output solution /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public string SolutionContainerId = "output"; /// - /// a whitelist for what entities can be inserted into this reclaimer + /// a whitelist for what entities can be inserted into this reclaimer /// [DataField] public EntityWhitelist? Whitelist; /// - /// a blacklist for what entities cannot be inserted into this reclaimer + /// a blacklist for what entities cannot be inserted into this reclaimer /// [DataField] public EntityWhitelist? Blacklist; /// - /// The sound played when something is being processed. + /// The sound played when something is being processed. /// [DataField] public SoundSpecifier? Sound; /// - /// whether or not we cut off the sound early when the reclaiming ends. + /// whether or not we cut off the sound early when the reclaiming ends. /// [DataField] public bool CutOffSound = true; /// - /// When the next sound will be allowed to be played. Used to prevent spam. + /// When the next sound will be allowed to be played. Used to prevent spam. /// [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] [AutoPausedField] public TimeSpan NextSound; /// - /// Minimum time inbetween each + /// Minimum time inbetween each /// [DataField] public TimeSpan SoundCooldown = TimeSpan.FromSeconds(0.8f); @@ -102,10 +123,10 @@ public sealed partial class MaterialReclaimerComponent : Component public EntityUid? Stream; /// - /// A counter of how many items have been processed + /// A counter of how many items have been processed /// /// - /// I saw this on the recycler and i'm porting it because it's cute af + /// I saw this on the recycler and i'm porting it because it's cute af /// [DataField, AutoNetworkedField] public int ItemsProcessed; diff --git a/Content.Shared/Psionics/Events.cs b/Content.Shared/Psionics/Events.cs index cf9a50c6e18..f110c9c405f 100644 --- a/Content.Shared/Psionics/Events.cs +++ b/Content.Shared/Psionics/Events.cs @@ -1,28 +1,59 @@ using Robust.Shared.Serialization; +using Content.Shared.Damage; using Content.Shared.DoAfter; -namespace Content.Shared.Psionics.Events +namespace Content.Shared.Psionics.Events; + +[Serializable, NetSerializable] +public sealed partial class PsionicRegenerationDoAfterEvent : DoAfterEvent { - [Serializable, NetSerializable] - public sealed partial class PsionicRegenerationDoAfterEvent : DoAfterEvent + [DataField("startedAt", required: true)] + public TimeSpan StartedAt; + + public PsionicRegenerationDoAfterEvent(TimeSpan startedAt) { - [DataField("startedAt", required: true)] - public TimeSpan StartedAt; + StartedAt = startedAt; + } + + public override DoAfterEvent Clone() => this; +} - private PsionicRegenerationDoAfterEvent() - { - } +[Serializable, NetSerializable] +public sealed partial class GlimmerWispDrainDoAfterEvent : SimpleDoAfterEvent { } - public PsionicRegenerationDoAfterEvent(TimeSpan startedAt) - { - StartedAt = startedAt; - } +[Serializable, NetSerializable] +public sealed partial class HealingWordDoAfterEvent : DoAfterEvent +{ + [DataField(required: true)] + public TimeSpan StartedAt; - public override DoAfterEvent Clone() => this; + public HealingWordDoAfterEvent(TimeSpan startedAt) + { + StartedAt = startedAt; } - [Serializable, NetSerializable] - public sealed partial class GlimmerWispDrainDoAfterEvent : SimpleDoAfterEvent + public override DoAfterEvent Clone() => this; +} + +[Serializable, NetSerializable] +public sealed partial class PsionicHealOtherDoAfterEvent : DoAfterEvent +{ + [DataField(required: true)] + public TimeSpan StartedAt; + + [DataField] + public DamageSpecifier HealingAmount = default!; + + [DataField] + public float RotReduction; + + [DataField] + public bool DoRevive; + + public PsionicHealOtherDoAfterEvent(TimeSpan startedAt) { + StartedAt = startedAt; } + + public override DoAfterEvent Clone() => this; } diff --git a/Content.Shared/Psionics/PsionicComponent.cs b/Content.Shared/Psionics/PsionicComponent.cs index 85b7e380fea..37d0a9a7ef4 100644 --- a/Content.Shared/Psionics/PsionicComponent.cs +++ b/Content.Shared/Psionics/PsionicComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.DoAfter; using Content.Shared.Psionics; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -166,6 +167,14 @@ private set /// unneccesary subs for unique psionic entities like e.g. Oracle. /// [DataField] - public List? PsychognomicDescriptors = null; + public List? PsychognomicDescriptors = null; + + /// Used for tracking what spell a Psion is actively casting + [DataField] + public DoAfterId? DoAfter; + + /// Popup to play if a Psion attempts to start casting a power while already casting one + [DataField] + public string AlreadyCasting = "already-casting"; } } diff --git a/Content.Shared/Singularity/Components/SharedEmitterComponent.cs b/Content.Shared/Singularity/Components/SharedEmitterComponent.cs index c2e7af717b1..cc6e8aeede2 100644 --- a/Content.Shared/Singularity/Components/SharedEmitterComponent.cs +++ b/Content.Shared/Singularity/Components/SharedEmitterComponent.cs @@ -1,4 +1,5 @@ using System.Threading; +using Content.Shared.Construction.Prototypes; using Content.Shared.DeviceLinking; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -14,90 +15,130 @@ public sealed partial class EmitterComponent : Component { public CancellationTokenSource? TimerCancel; - // whether the power switch is in "on" - [ViewVariables] public bool IsOn; - // Whether the power switch is on AND the machine has enough power (so is actively firing) - [ViewVariables] public bool IsPowered; + /// + /// Whether the power switch is on + /// + [ViewVariables] + public bool IsOn; /// - /// counts the number of consecutive shots fired. + /// Whether the power switch is on AND the machine has enough power (so is actively firing) + /// + [ViewVariables] + public bool IsPowered; + + /// + /// counts the number of consecutive shots fired. /// [ViewVariables] public int FireShotCounter; /// - /// The entity that is spawned when the emitter fires. + /// The entity that is spawned when the emitter fires. /// - [DataField("boltType", customTypeSerializer: typeof(PrototypeIdSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string BoltType = "EmitterBolt"; - [DataField("selectableTypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] public List SelectableTypes = new(); /// - /// The current amount of power being used. + /// The current amount of power being used. /// - [DataField("powerUseActive")] + [DataField] public int PowerUseActive = 600; /// - /// The amount of shots that are fired in a single "burst" + /// The amount of shots that are fired in a single "burst" /// - [DataField("fireBurstSize")] + [DataField] public int FireBurstSize = 3; /// - /// The time between each shot during a burst. + /// The time between each shot during a burst. /// - [DataField("fireInterval")] + [DataField] public TimeSpan FireInterval = TimeSpan.FromSeconds(2); /// - /// The current minimum delay between bursts. + /// The base amount of time between each shot during a burst. /// - [DataField("fireBurstDelayMin")] + [DataField] + public TimeSpan BaseFireInterval = TimeSpan.FromSeconds(2); + + /// + /// The current minimum delay between bursts. + /// + [DataField] public TimeSpan FireBurstDelayMin = TimeSpan.FromSeconds(4); /// - /// The current maximum delay between bursts. + /// The current maximum delay between bursts. /// - [DataField("fireBurstDelayMax")] + [DataField] public TimeSpan FireBurstDelayMax = TimeSpan.FromSeconds(10); /// - /// The visual state that is set when the emitter is turned on + /// The base minimum delay between shot bursts. + /// Used for machine part rating calculations. + /// + [DataField] + public TimeSpan BaseFireBurstDelayMin = TimeSpan.FromSeconds(4); + + /// + /// The base maximum delay between shot bursts. + /// Used for machine part rating calculations. + /// + [DataField] + public TimeSpan BaseFireBurstDelayMax = TimeSpan.FromSeconds(10); + + /// + /// The multiplier for the base delay between shot bursts as well as + /// the fire interval + /// + [DataField] + public float FireRateMultiplier = 0.8f; + + /// + /// The machine part that affects burst delay. + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MachinePartFireRate = "Capacitor"; + + /// + /// The visual state that is set when the emitter is turned on /// - [DataField("onState")] + [DataField] public string? OnState = "beam"; /// - /// The visual state that is set when the emitter doesn't have enough power. + /// The visual state that is set when the emitter doesn't have enough power. /// - [DataField("underpoweredState")] + [DataField] public string? UnderpoweredState = "underpowered"; /// - /// Signal port that turns on the emitter. + /// Signal port that turns on the emitter. /// - [DataField("onPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string OnPort = "On"; /// - /// Signal port that turns off the emitter. + /// Signal port that turns off the emitter. /// - [DataField("offPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string OffPort = "Off"; /// - /// Signal port that toggles the emitter on or off. + /// Signal port that toggles the emitter on or off. /// - [DataField("togglePort", customTypeSerializer: typeof(PrototypeIdSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string TogglePort = "Toggle"; /// - /// Map of signal ports to entity prototype IDs of the entity that will be fired. + /// Map of signal ports to entity prototype IDs of the entity that will be fired. /// - [DataField("setTypePorts", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] public Dictionary SetTypePorts = new(); } diff --git a/Content.Shared/Standing/LayingDownComponent.cs b/Content.Shared/Standing/LayingDownComponent.cs index 1499704c53b..363c6f15987 100644 --- a/Content.Shared/Standing/LayingDownComponent.cs +++ b/Content.Shared/Standing/LayingDownComponent.cs @@ -14,6 +14,9 @@ public sealed partial class LayingDownComponent : Component [DataField, AutoNetworkedField] public bool AutoGetUp; + + [DataField, AutoNetworkedField] + public int? OriginalDrawDepth { get; set; } } [Serializable, NetSerializable] @@ -24,3 +27,9 @@ public sealed class CheckAutoGetUpEvent(NetEntity user) : CancellableEntityEvent { public NetEntity User = user; } + +[Serializable, NetSerializable] +public sealed class DrawDownedEvent(NetEntity uid) : EntityEventArgs +{ + public NetEntity Uid = uid; +} diff --git a/Content.Shared/Standing/StandingStateSystem.cs b/Content.Shared/Standing/StandingStateSystem.cs index 69e72033bba..04c0a04547d 100644 --- a/Content.Shared/Standing/StandingStateSystem.cs +++ b/Content.Shared/Standing/StandingStateSystem.cs @@ -1,12 +1,15 @@ using Content.Shared.Buckle; using Content.Shared.Buckle.Components; +using Content.Shared.CCVar; using Content.Shared.Hands.Components; using Content.Shared.Movement.Systems; using Content.Shared.Physics; using Content.Shared.Rotation; using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; +using Robust.Shared.Serialization; namespace Content.Shared.Standing; @@ -17,6 +20,7 @@ public sealed class StandingStateSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; [Dependency] private readonly SharedBuckleSystem _buckle = default!; + [Dependency] private readonly IConfigurationManager _config = default!; // If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited. private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable; @@ -64,6 +68,10 @@ public bool Down(EntityUid uid, bool playSound = true, bool dropHeldItems = true Dirty(standingState); RaiseLocalEvent(uid, new DownedEvent(), false); + // Raising this event will lower the entity's draw depth to the same as a small mob. + if (_config.GetCVar(CCVars.CrawlUnderTables)) + RaiseNetworkEvent(new DrawDownedEvent(GetNetEntity(uid))); + // Seemed like the best place to put it _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance); diff --git a/Content.Shared/Store/ListingLocalisationHelpers.cs b/Content.Shared/Store/ListingLocalisationHelpers.cs index 882300109ce..19cd029488f 100644 --- a/Content.Shared/Store/ListingLocalisationHelpers.cs +++ b/Content.Shared/Store/ListingLocalisationHelpers.cs @@ -18,6 +18,11 @@ public static string GetLocalisedNameOrEntityName(ListingData listingData, IProt else if (listingData.ProductEntity != null) name = prototypeManager.Index(listingData.ProductEntity.Value).Name; + if (listingData.DiscountValue > 0) + name += " " + Loc.GetString("store-sales-amount", ("amount", listingData.DiscountValue)); + else if (listingData.OldCost.Count > 0) + name += " " + Loc.GetString("store-sales-over"); + return name; } diff --git a/Content.Shared/Store/ListingPrototype.cs b/Content.Shared/Store/ListingPrototype.cs index d3d2e13cdfd..0b59ab48cb6 100644 --- a/Content.Shared/Store/ListingPrototype.cs +++ b/Content.Shared/Store/ListingPrototype.cs @@ -109,6 +109,19 @@ public partial class ListingData : IEquatable, ICloneable [DataField] public TimeSpan RestockTime = TimeSpan.Zero; + [DataField] + public int SaleLimit = 3; + + [DataField] + public bool SaleBlacklist; + + public int DiscountValue; + + public Dictionary, FixedPoint2> OldCost = new(); + + [DataField] + public List Components = new(); + public bool Equals(ListingData? listing) { if (listing == null) @@ -166,6 +179,11 @@ public object Clone() ProductEvent = ProductEvent, PurchaseAmount = PurchaseAmount, RestockTime = RestockTime, + SaleLimit = SaleLimit, + SaleBlacklist = SaleBlacklist, + DiscountValue = DiscountValue, + OldCost = OldCost, + Components = Components, }; } } diff --git a/Content.Shared/Store/StorePresetPrototype.cs b/Content.Shared/Store/StorePresetPrototype.cs index ce7f0312b60..41ee510bd8e 100644 --- a/Content.Shared/Store/StorePresetPrototype.cs +++ b/Content.Shared/Store/StorePresetPrototype.cs @@ -1,3 +1,4 @@ +using Content.Shared.StoreDiscount; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; @@ -38,4 +39,7 @@ public sealed partial class StorePresetPrototype : IPrototype /// [DataField("currencyWhitelist", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] public HashSet CurrencyWhitelist { get; private set; } = new(); + + [DataField] + public SalesSpecifier Sales { get; private set; } = new(); } diff --git a/Content.Shared/StoreDiscount/SalesSpecifier.cs b/Content.Shared/StoreDiscount/SalesSpecifier.cs new file mode 100644 index 00000000000..21539f85d41 --- /dev/null +++ b/Content.Shared/StoreDiscount/SalesSpecifier.cs @@ -0,0 +1,38 @@ +namespace Content.Shared.StoreDiscount; + +[DataDefinition] +public sealed partial class SalesSpecifier +{ + [DataField] + public bool Enabled { get; private set; } + + [DataField] + public float MinMultiplier { get; private set; } + + [DataField] + public float MaxMultiplier { get; private set; } + + [DataField] + public int MinItems { get; private set; } + + [DataField] + public int MaxItems { get; private set; } + + [DataField] + public string SalesCategory { get; private set; } = string.Empty; + + public SalesSpecifier() + { + } + + public SalesSpecifier(bool enabled, float minMultiplier, float maxMultiplier, int minItems, int maxItems, + string salesCategory) + { + Enabled = enabled; + MinMultiplier = minMultiplier; + MaxMultiplier = maxMultiplier; + MinItems = minItems; + MaxItems = maxItems; + SalesCategory = salesCategory; + } +} diff --git a/Content.Shared/Traits/Prototypes/TraitPrototype.cs b/Content.Shared/Traits/Prototypes/TraitPrototype.cs index cd4b02a1e63..7c0e429a691 100644 --- a/Content.Shared/Traits/Prototypes/TraitPrototype.cs +++ b/Content.Shared/Traits/Prototypes/TraitPrototype.cs @@ -58,6 +58,30 @@ public sealed partial class TraitPrototype : IPrototype [DataField] public List? PsionicPowers { get; private set; } = default!; + /// + /// The list of all Spoken Languages that this trait adds. + /// + [DataField] + public List? LanguagesSpoken { get; private set; } = default!; + + /// + /// The list of all Understood Languages that this trait adds. + /// + [DataField] + public List? LanguagesUnderstood { get; private set; } = default!; + + /// + /// The list of all Spoken Languages that this trait removes. + /// + [DataField] + public List? RemoveLanguagesSpoken { get; private set; } = default!; + + /// + /// The list of all Understood Languages that this trait removes. + /// + [DataField] + public List? RemoveLanguagesUnderstood { get; private set; } = default!; + /// /// The list of all Moodlets that this trait adds. /// diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs index c242d448f27..f86bfc32ed3 100644 --- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs +++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Contests; using Content.Shared.Damage; using Content.Shared.FixedPoint; using Robust.Shared.Audio; @@ -109,7 +110,7 @@ public sealed partial class MeleeWeaponComponent : Component /// Total width of the angle for wide attacks. /// [DataField, AutoNetworkedField] - public Angle Angle = Angle.FromDegrees(45); + public Angle Angle = Angle.FromDegrees(60); [DataField, AutoNetworkedField] public EntProtoId Animation = "WeaponArcPunch"; @@ -128,10 +129,10 @@ public sealed partial class MeleeWeaponComponent : Component public bool SwingLeft; [DataField, AutoNetworkedField] - public float HeavyStaminaCost = 10f; + public float HeavyStaminaCost = 2.5f; [DataField, AutoNetworkedField] - public int MaxTargets = 1; + public int MaxTargets = 3; // Sounds @@ -156,6 +157,20 @@ public sealed partial class MeleeWeaponComponent : Component /// [DataField, AutoNetworkedField] public SoundSpecifier SoundNoDamage { get; set; } = new SoundCollectionSpecifier("WeakHit"); + + /// + /// Arguments for the MeleeContestInteractions constructor + /// + [DataField] + public ContestArgs ContestArgs = new ContestArgs + { + DoStaminaInteraction = true, + StaminaDisadvantage = true, + StaminaRangeModifier = 2, + StaminaOffset = 0.25f, + DoHealthInteraction = true, + HealthRangeModifier = 1.5f, + }; } /// diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index b5a537b7e15..fd77ad31a45 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -32,7 +32,7 @@ namespace Content.Shared.Weapons.Melee; -public abstract class SharedMeleeWeaponSystem : EntitySystem +public abstract partial class SharedMeleeWeaponSystem : EntitySystem { [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; [Dependency] protected readonly ActionBlockerSystem Blocker = default!; @@ -225,6 +225,9 @@ public DamageSpecifier GetDamage(EntityUid uid, EntityUid user, MeleeWeaponCompo var ev = new GetMeleeDamageEvent(uid, new (component.Damage), new(), user); RaiseLocalEvent(uid, ref ev); + if (component.ContestArgs is not null) + ev.Damage *= _contests.ContestConstructor(user, component.ContestArgs); + return DamageSpecifier.ApplyModifierSets(ev.Damage, ev.Modifiers); } @@ -249,9 +252,7 @@ public FixedPoint2 GetHeavyDamageModifier(EntityUid uid, EntityUid user, MeleeWe return ev.DamageModifier * ev.Multipliers - * component.HeavyDamageBaseModifier - * _contests.StaminaContest(user, false, 2f) //Taking stamina damage reduces wide swing damage by up to 50% - / _contests.HealthContest(user, false, 0.8f); //Being injured grants up to 20% more wide swing damage + * component.HeavyDamageBaseModifier; } public bool TryGetWeapon(EntityUid entity, out EntityUid weaponUid, [NotNullWhen(true)] out MeleeWeaponComponent? melee) @@ -440,9 +441,7 @@ private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponCompo protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, EntityUid meleeUid, MeleeWeaponComponent component, ICommonSession? session) { - var damage = GetDamage(meleeUid, user, component) - * _contests.StaminaContest(user) //Taking stamina damage reduces light attack damage by up to 25% - / _contests.HealthContest(user, false, 0.8f); //Being injured grants up to 20% more damage; + var damage = GetDamage(meleeUid, user, component); var target = GetEntity(ev.Target); // For consistency with wide attacks stuff needs damageable. diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index a233a61a2d4..f85816dfafa 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -6467,3 +6467,163 @@ Entries: id: 6372 time: '2024-09-19T20:37:51.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/933 +- author: VMSolidus + changes: + - type: Fix + message: >- + UniversalLanguageSpeaker(And Xenoglossy by extension) will now appear in + your language menu alongside other known languages, rather than replace + all known languages. You can effectively now choose whether or not to + speak it, or to use a normal language. + - type: Add + message: Traits can now add Languages directly. + - type: Add + message: Traits can now remove Languages directly. + id: 6373 + time: '2024-09-20T01:47:52.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/899 +- author: VMSolidus + changes: + - type: Add + message: >- + Melee Weapons can now individually define their interactions with the + ContestsSystem. + - type: Add + message: >- + Added the ContestArgs type, allowing arbitrarily any component to + contain a list of arguments for ContestSystems. + - type: Add + message: >- + Added the ContestConstructor, a new type of meta-Contest that enables + other systems to use components to define all possible contest + behaviors, rather than needing to hardcode specific interactions. + id: 6374 + time: '2024-09-20T02:48:43.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/934 +- author: gluesniffler + changes: + - type: Add + message: >- + Adds an optional server variable which allows entities to crawl under + tables. + - type: Tweak + message: >- + Tables and plastic flaps are less resistant to damage, and can now be + targeted by guns by aiming on top of them. + id: 6375 + time: '2024-09-20T19:34:02.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/939 +- author: ODJ + changes: + - type: Tweak + message: Tweaked melee; Less stamina usage on heavy attacks. + id: 6376 + time: '2024-09-20T19:34:46.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/938 +- author: VMSolidus + changes: + - type: Tweak + message: >- + JukeOperator now allows for JukeDuration and JukeCooldown arguments. + JukeCooldown is a new feature where enemies must wait an amount of time + in seconds equal to the JukeCooldown, before they are allowed to attempt + to dodge a melee attack. + - type: Tweak + message: >- + By default, NPCs will only attempt to dodge attacks once every 5 + seconds. + - type: Fix + message: >- + JukeOperator now performs extremely expensive math 5000 times less + often. EXIT CONDITIONS PEOPLE! + id: 6377 + time: '2024-09-20T20:05:31.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/935 +- author: VMSolidus + changes: + - type: Add + message: >- + All "Long-arms", Rifles, Light Machine Guns, Shotguns, now require + wielding to use. + id: 6378 + time: '2024-09-20T20:35:43.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/913 +- author: zelezniciar + changes: + - type: Add + message: Added Atmospheric Alerts Computer + id: 6379 + time: '2024-09-20T21:46:38.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/922 +- author: VMSolidus + changes: + - type: Add + message: 'A basic Languages menu has been added into the Traits tab. ' + - type: Add + message: >- + Tau-Ceti Basic, Tradeband, Freespeak, Elyran Standard, Sol Common, and + Sign Language have been added to the Languages tab. + id: 6380 + time: '2024-09-20T21:46:47.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/936 +- author: VMSolidus + changes: + - type: Remove + message: >- + Removed the "Become Psionic" traitor objective. It was literally + impossible to fail if you took the Latent Psychic trait, and literally + impossible to greentext if you didn't. + id: 6381 + time: '2024-09-20T21:47:16.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/940 +- author: VMSolidus + changes: + - type: Add + message: Part Upgrading has returned! + id: 6382 + time: '2024-09-21T22:46:49.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/917 +- author: Spatison + changes: + - type: Add + message: Added discounts in uplink / Добавлены скидки в аплинк + id: 6383 + time: '2024-09-21T22:46:59.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/930 +- author: VMSolidus + changes: + - type: Add + message: >- + Healing Word has been added as a new Psionic Power. When cast on another + person, it heals a small amount of every damage type(scaling with + Casting Stats), while also reducing rot timers. Healing Word has a very + short cooldown, and a fairly low Glimmer cost. + - type: Add + message: >- + Breath of Life has been added as a new extremely rare Psionic Power. + When cast on another person, it heals a large amount of damage(scaling + with Casting Stats), while also substantially reducing rot timers. + Additionally, it will revive the target if it is possible to do so. + Breath of Life has an incredibly long cooldown, a long interuptable cast + time, and an extraordinarily high glimmer cost(A typical Psion will + spike glimmer by more than 50 points when casting it). + - type: Add + message: The Chaplain now starts with the Healing Word power. + id: 6384 + time: '2024-09-21T22:50:01.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/942 +- author: VMSolidus + changes: + - type: Add + message: >- + Bottles, Drink Glasses, Plates, and all liquid containers are now struck + by bullets(and most likely destroyed in the process). We hope that this + will offer both a small tactical advantage/disadvantage, as well as + contribute to making gunfights around the bar more "Cinematic". + - type: Add + message: >- + Chairs are now hit by projectiles if a shooter clicks on them, in + addition to Tables. + id: 6385 + time: '2024-09-21T23:22:43.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/943 diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index 1a2d582a05b..aeed9636628 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, africalimedrop, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, angelofallars, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, BasedUser, beck-thompson, BGare, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlueHNT, Boaz1111, BobdaBiscuit, brainfood1183, BramvanZijp, Brandon-Huu, Bribrooo, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CaasGit, CakeQ, CaptainSqrBeard, Carbonhell, Carolyn3114, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, clorl, Clyybber, CodedCrow, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, Deeeeja, deepdarkdepths, Delete69, deltanedas, DeltaV-Bot, DerbyX, DoctorBeard, DogZeroX, dontbetank, dootythefrooty, Doru991, DoubleRiceEddiedd, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, Evgencheg, exincore, exp111, Fahasor, FairlySadPanda, Fansana, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, FoxxoTrystan, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, geraeumig, Git-Nivrak, github-actions[bot], gituhabu, gluesniffler, GNF54, Golinth, GoodWheatley, graevy, GreyMario, Guess-My-Name, gusxyz, h3half, Hanzdegloker, Hardly3D, harikattar, HerCoyote23, HoofedEar, Hoolny, hord-brayden, hubismal, Hugal31, Huxellberger, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, Interrobang01, IProduceWidgets, ItsMeThom, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTrotter, KaiShibaa, kalane15, kalanosh, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, leonardo-dabepis, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, LovelyLophi, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, Mnemotechnician, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, notafet, notquitehadouken, noudoit, noverd, nuke-haus, NULL882, nyeogmi, OCOtheOmega, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, PHCodes, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykzz, PuroSlavKing, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, ShadowCommander, Shadowtheprotogen546, ShatteredSwords, SignalWalker, SimpleStation14, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, SleepyScarecrow, Snowni, snowsignal, SonicHDC, SoulSloth, SpaceManiac, SpeltIncorrectyl, spoogemonster, ssdaniel24, stalengd, Stealthbomber16, stellar-novas, StrawberryMoses, superjj18, SweptWasTaken, Szunti, TadJohnson00, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, Tmanzxd, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UKNOWH, UnicornOnLSD, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, VMSolidus, volundr-, Voomra, Vordenburg, vulppine, wafehling, WarMechanic, waylon531, weaversam8, whateverusername0, Willhelm53, Winkarst-cpu, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zumorica, Zymem +0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, africalimedrop, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, angelofallars, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, BasedUser, beck-thompson, BGare, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlueHNT, Boaz1111, BobdaBiscuit, brainfood1183, BramvanZijp, Brandon-Huu, Bribrooo, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CaasGit, CakeQ, CaptainSqrBeard, Carbonhell, Carolyn3114, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, CilliePaint, clorl, Clyybber, CodedCrow, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, Deeeeja, deepdarkdepths, Delete69, deltanedas, DeltaV-Bot, DerbyX, DoctorBeard, DogZeroX, dontbetank, dootythefrooty, Doru991, DoubleRiceEddiedd, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, Evgencheg, exincore, exp111, Fahasor, FairlySadPanda, Fansana, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, FoxxoTrystan, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, geraeumig, Git-Nivrak, github-actions[bot], gituhabu, gluesniffler, GNF54, Golinth, GoodWheatley, graevy, GreyMario, Guess-My-Name, gusxyz, h3half, Hanzdegloker, Hardly3D, harikattar, HerCoyote23, HoofedEar, Hoolny, hord-brayden, hubismal, Hugal31, Huxellberger, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, Interrobang01, IProduceWidgets, ItsMeThom, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTrotter, KaiShibaa, kalane15, kalanosh, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, leonardo-dabepis, LetterN, Level10Cybermancer, lever1209, Lgibb18, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, LovelyLophi, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, Mnemotechnician, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, notafet, notquitehadouken, noudoit, nuke-haus, NULL882, nyeogmi, OCOtheOmega, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, PHCodes, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykzz, PuroSlavKing, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, ShadowCommander, Shadowtheprotogen546, ShatteredSwords, SignalWalker, SimpleStation14, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, SleepyScarecrow, Snowni, snowsignal, SonicHDC, SoulSloth, SpaceManiac, SpeltIncorrectyl, spoogemonster, ssdaniel24, stalengd, Stealthbomber16, stellar-novas, StrawberryMoses, superjj18, SweptWasTaken, Szunti, TadJohnson00, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, Tmanzxd, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UKNOWH, UltimateJester, UnicornOnLSD, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, VMSolidus, volundr-, Voomra, Vordenburg, vulppine, wafehling, WarMechanic, waylon531, weaversam8, whateverusername0, Willhelm53, Winkarst-cpu, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, ZNixian, ZoldorfTheWizard, Zumorica, Zymem diff --git a/Resources/Locale/en-US/anomaly/anomaly.ftl b/Resources/Locale/en-US/anomaly/anomaly.ftl index da5882fa62f..1609d77d914 100644 --- a/Resources/Locale/en-US/anomaly/anomaly.ftl +++ b/Resources/Locale/en-US/anomaly/anomaly.ftl @@ -3,6 +3,7 @@ anomaly-component-contact-damage = The anomaly sears off your skin! anomaly-vessel-component-anomaly-assigned = Anomaly assigned to vessel. anomaly-vessel-component-not-assigned = This vessel is not assigned to any anomaly. Try using a scanner on it. anomaly-vessel-component-assigned = This vessel is currently assigned to an anomaly. +anomaly-vessel-component-upgrade-output = point output anomaly-particles-delta = Delta particles anomaly-particles-epsilon = Epsilon particles diff --git a/Resources/Locale/en-US/atmos/atmos-alerts-console.ftl b/Resources/Locale/en-US/atmos/atmos-alerts-console.ftl new file mode 100644 index 00000000000..a1640c5e9d5 --- /dev/null +++ b/Resources/Locale/en-US/atmos/atmos-alerts-console.ftl @@ -0,0 +1,35 @@ +atmos-alerts-window-title = Atmospheric Alerts Computer +atmos-alerts-window-station-name = [color=white][font size=14]{$stationName}[/font][/color] +atmos-alerts-window-unknown-location = Unknown location + +atmos-alerts-window-tab-no-alerts = Alerts +atmos-alerts-window-tab-alerts = Alerts ({$value}) +atmos-alerts-window-tab-air-alarms = Air alarms +atmos-alerts-window-tab-fire-alarms = Fire alarms + +atmos-alerts-window-alarm-label = {CAPITALIZE($name)} ({$address}) +atmos-alerts-window-temperature-label = Temperature +atmos-alerts-window-temperature-value = {$valueInC} °C ({$valueInK} K) +atmos-alerts-window-pressure-label = Pressure +atmos-alerts-window-pressure-value = {$value} kPa +atmos-alerts-window-oxygenation-label = Oxygenation +atmos-alerts-window-oxygenation-value = {$value}% +atmos-alerts-window-other-gases-label = Other present gases +atmos-alerts-window-other-gases-value = {$shorthand} ({$value}%) +atmos-alerts-window-other-gases-value-nil = None +atmos-alerts-window-silence-alerts = Silence alerts from this alarm + +atmos-alerts-window-label-alert-types = Alert levels: +atmos-alerts-window-normal-state = Normal +atmos-alerts-window-warning-state = Warning +atmos-alerts-window-danger-state = Danger! +atmos-alerts-window-invalid-state = Inactive + +atmos-alerts-window-no-active-alerts = [font size=16][color=white]No active alerts -[/color] [color={$color}]situation normal[/color][/font] +atmos-alerts-window-no-data-available = No data available +atmos-alerts-window-alerts-being-silenced = Silencing alerts... + +atmos-alerts-window-toggle-overlays = Toggle alarm display + +atmos-alerts-window-flavor-left = Contact an atmospheric technician for assistance +atmos-alerts-window-flavor-right = v1.8 \ No newline at end of file diff --git a/Resources/Locale/en-US/atmos/gas-recycler-system.ftl b/Resources/Locale/en-US/atmos/gas-recycler-system.ftl index cc527adf5c8..a72e137732c 100644 --- a/Resources/Locale/en-US/atmos/gas-recycler-system.ftl +++ b/Resources/Locale/en-US/atmos/gas-recycler-system.ftl @@ -1,3 +1,6 @@ gas-recycler-reacting = It is [color=green]converting[/color] waste gases. gas-recycler-low-pressure = The input pressure is [color=darkred]too low[/color]. gas-recycler-low-temperature = The input temperature is [color=darkred]too low[/color]. + +gas-recycler-upgrade-min-temp = Minimum temperature +gas-recycler-upgrade-min-pressure = Minimum pressure diff --git a/Resources/Locale/en-US/atmos/portable-scrubber.ftl b/Resources/Locale/en-US/atmos/portable-scrubber.ftl index c4071b4acce..8aadf076d96 100644 --- a/Resources/Locale/en-US/atmos/portable-scrubber.ftl +++ b/Resources/Locale/en-US/atmos/portable-scrubber.ftl @@ -1 +1,4 @@ portable-scrubber-fill-level = It's at about [color=yellow]{$percent}%[/color] of its maximum internal pressure. + +portable-scrubber-component-upgrade-max-pressure = max pressure +portable-scrubber-component-upgrade-transfer-rate = transfer rate diff --git a/Resources/Locale/en-US/botany/components/seed-extractor-component.ftl b/Resources/Locale/en-US/botany/components/seed-extractor-component.ftl index 84d5a5ed283..c586a594a9a 100644 --- a/Resources/Locale/en-US/botany/components/seed-extractor-component.ftl +++ b/Resources/Locale/en-US/botany/components/seed-extractor-component.ftl @@ -2,3 +2,5 @@ seed-extractor-component-interact-message = You extract some seeds from the { THE($name) }. seed-extractor-component-no-seeds = { CAPITALIZE(THE($name)) } has no seeds! + +seed-extractor-component-upgrade-seed-yield = seed yield diff --git a/Resources/Locale/en-US/cargo/cargo-console-component.ftl b/Resources/Locale/en-US/cargo/cargo-console-component.ftl index b56f4730ccd..1caa810f1b7 100644 --- a/Resources/Locale/en-US/cargo/cargo-console-component.ftl +++ b/Resources/Locale/en-US/cargo/cargo-console-component.ftl @@ -45,3 +45,5 @@ cargo-shuttle-console-station-unknown = Unknown cargo-shuttle-console-shuttle-not-found = Not found cargo-shuttle-console-organics = Detected organic lifeforms on the shuttle cargo-no-shuttle = No cargo shuttle found! + +cargo-telepad-delay-upgrade = Teleport delay diff --git a/Resources/Locale/en-US/chemistry/components/solution-heater-component.ftl b/Resources/Locale/en-US/chemistry/components/solution-heater-component.ftl new file mode 100644 index 00000000000..cecf15550c7 --- /dev/null +++ b/Resources/Locale/en-US/chemistry/components/solution-heater-component.ftl @@ -0,0 +1 @@ +solution-heater-upgrade-heat = Heat strength diff --git a/Resources/Locale/en-US/construction/components/machine-part-component.ftl b/Resources/Locale/en-US/construction/components/machine-part-component.ftl new file mode 100644 index 00000000000..0613f505161 --- /dev/null +++ b/Resources/Locale/en-US/construction/components/machine-part-component.ftl @@ -0,0 +1,2 @@ +machine-part-component-on-examine-rating-text = [color=white]Rating:[/color] [color=cyan]{$rating}[/color] +machine-part-component-on-examine-type-text = [color=white]Type:[/color] [color=cyan]{$type}[/color] diff --git a/Resources/Locale/en-US/flight/flight_system.ftl b/Resources/Locale/en-US/flight/flight_system.ftl index 12693cc8467..3558ab9dc88 100644 --- a/Resources/Locale/en-US/flight/flight_system.ftl +++ b/Resources/Locale/en-US/flight/flight_system.ftl @@ -1,2 +1,3 @@ no-flight-while-restrained = You can't fly right now. -no-flight-while-zombified = You can't use your wings right now. \ No newline at end of file +no-flight-while-zombified = You can't use your wings right now. +no-flight-while-lying = You can't fly right now. \ No newline at end of file diff --git a/Resources/Locale/en-US/kitchen/components/microwave-component.ftl b/Resources/Locale/en-US/kitchen/components/microwave-component.ftl index 0603b3c8463..12346ee75dc 100644 --- a/Resources/Locale/en-US/kitchen/components/microwave-component.ftl +++ b/Resources/Locale/en-US/kitchen/components/microwave-component.ftl @@ -9,6 +9,7 @@ microwave-component-suicide-multi-head-others-message = {$victim} is trying to c microwave-component-suicide-others-message = {$victim} is trying to cook their head! microwave-component-suicide-multi-head-message = You cook your heads! microwave-component-suicide-message = You cook your head! +microwave-component-upgrade-cook-time = cook time microwave-component-interact-full = It's full. microwave-component-interact-item-too-big = { CAPITALIZE(THE($item)) } is too big to fit in the microwave! diff --git a/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl b/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl index 8a3ca9eef85..34951282745 100644 --- a/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl +++ b/Resources/Locale/en-US/kitchen/components/reagent-grinder-component.ftl @@ -4,6 +4,9 @@ reagent-grinder-bound-user-interface-instant-button = INSTANT reagent-grinder-bound-user-interface-cook-time-label = COOK TIME reagent-grinder-component-cannot-put-entity-message = You can't put this in the reagent grinder! +reagent-grinder-component-upgrade-work-time = Work time +reagent-grinder-component-upgrade-storage = Storage + grinder-menu-title = All-In-One Grinder 3000 grinder-menu-grind-button = Grind grinder-menu-juice-button = Juice diff --git a/Resources/Locale/en-US/language/commands.ftl b/Resources/Locale/en-US/language/commands.ftl index 65959e3f28f..ccbe2570c96 100644 --- a/Resources/Locale/en-US/language/commands.ftl +++ b/Resources/Locale/en-US/language/commands.ftl @@ -2,10 +2,10 @@ command-list-langs-desc = List languages your current entity can speak at the cu command-list-langs-help = Usage: {$command} command-saylang-desc = Send a message in a specific language. To choose a language, you can use either the name of the language, or its position in the list of languages. -command-saylang-help = Usage: {$command} . Example: {$command} GalacticCommon "Hello World!". Example: {$command} 1 "Hello World!" +command-saylang-help = Usage: {$command} . Example: {$command} TauCetiBasic "Hello World!". Example: {$command} 1 "Hello World!" command-language-select-desc = Select the currently spoken language of your entity. You can use either the name of the language, or its position in the list of languages. -command-language-select-help = Usage: {$command} . Example: {$command} 1. Example: {$command} GalacticCommon +command-language-select-help = Usage: {$command} . Example: {$command} 1. Example: {$command} TauCetiBasic command-language-spoken = Spoken: command-language-understood = Understood: @@ -18,14 +18,14 @@ command-language-invalid-language = The language {$id} does not exist or you can # toolshed command-description-language-add = Adds a new language to the piped entity. The two last arguments indicate whether it should be spoken/understood. Example: 'self language:add "Canilunzt" true true' -command-description-language-rm = Removes a language from the piped entity. Works similarly to language:add. Example: 'self language:rm "GalacticCommon" true true'. +command-description-language-rm = Removes a language from the piped entity. Works similarly to language:add. Example: 'self language:rm "TauCetiBasic" true true'. command-description-language-lsspoken = Lists all languages the entity can speak. Example: 'self language:lsspoken' command-description-language-lsunderstood = Lists all languages the entity can understand. Example: 'self language:lssunderstood' command-description-translator-addlang = Adds a new target language to the piped translator entity. See language:add for details. command-description-translator-rmlang = Removes a target language from the piped translator entity. See language:rm for details. -command-description-translator-addrequired = Adds a new required language to the piped translator entity. Example: 'ent 1234 translator:addrequired "GalacticCommon"' -command-description-translator-rmrequired = Removes a required language from the piped translator entity. Example: 'ent 1234 translator:rmrequired "GalacticCommon"' +command-description-translator-addrequired = Adds a new required language to the piped translator entity. Example: 'ent 1234 translator:addrequired "TauCetiBasic"' +command-description-translator-rmrequired = Removes a required language from the piped translator entity. Example: 'ent 1234 translator:rmrequired "TauCetiBasic"' command-description-translator-lsspoken = Lists all spoken languages for the piped translator entity. Example: 'ent 1234 translator:lsspoken' command-description-translator-lsunderstood = Lists all understood languages for the piped translator entity. Example: 'ent 1234 translator:lssunderstood' command-description-translator-lsrequired = Lists all required languages for the piped translator entity. Example: 'ent 1234 translator:lsrequired' diff --git a/Resources/Locale/en-US/language/languages.ftl b/Resources/Locale/en-US/language/languages.ftl index 68dc80f51da..7ad66d1aabe 100644 --- a/Resources/Locale/en-US/language/languages.ftl +++ b/Resources/Locale/en-US/language/languages.ftl @@ -1,9 +1,6 @@ language-Universal-name = Universal language-Universal-description = What are you? -language-GalacticCommon-name = Galactic common -language-GalacticCommon-description = The standard Galatic language, most commonly used for inter-species communications and legal work. - language-Bubblish-name = Bubblish language-Bubblish-description = The language of Slimes. Being a mixture of bubbling noises and pops it's very difficult to speak for humans without the use of mechanical aids. @@ -17,10 +14,37 @@ language-Draconic-name = Draconic language-Draconic-description = The common language of lizard-people, composed of sibilant hisses and rattles. language-SolCommon-name = Sol common -language-SolCommon-description = The language common to species from the Sol System. +language-SolCommon-description = + With its roots in Mandarin Chinese - Common evolved as the official language of the Sol Alliance - with officials working to tie it together with a common tongue. + It's spoken by state officials - taught in schools - and spoken by those who either feel a sense of national pride in the Alliance or otherwise fell sway to the culture. + +language-TauCetiBasic-name = Tau-Ceti Basic +language-TauCetiBasic-description = + A spiritual successor of Esperanto, established in 2404 in Tau Ceti by Ceti intellectuals. + Its unique, fully customized alphabet and structure allow it to be spoken even by most alien species. + It's the official language of Tau Ceti and has growing traction in diplomatic circles and Universalists across human space. + +language-Tradeband-name = Tradeband +language-Tradeband-description = + Descended from latin and romance languages of old Earth - Tradeband remains the main tongue of the upper class of humanity. + The language sounds elegant and well structured to most ears. It remains in popular use with traders - diplomats - and those seeking to hold onto a piece of a romantic past. + +language-Freespeak-name = Freespeak +language-Freespeak-description = + A language of renegades and frontiersmen descending from various languages from Earth-- like Hindi, + combined into a multi-rooted jumble that sounds incoherent or even barbarian to non-native speakers. + This language is the only common cultural identity for humans in the frontier. Speaking this language in itself boldly declares the speaker a free spirit. + It is often called 'Gutter' by Alliance citizens. + +language-Elyran-name = Elyran Standard +language-Elyran-description = + Elyran Standard is the official tongue of the Republic of Elyra. Constructed using elements of Farsi - Arabic - and Turkish. + Influence from all three of these languages can be seen throughout its grammar and vocabulary. language-Canilunzt-name = Canilunzt -language-Canilunzt-description = The guttural language spoken and utilized by the inhabitants of the Vazzend system, composed of growls, barks, yaps, and heavy utilization of ears and tail movements. Vulpkanin speak this language with ease. +language-Canilunzt-description = + The guttural language spoken and utilized by the inhabitants of the Vazzend system, + composed of growls, barks, yaps, and heavy utilization of ears and tail movements. Vulpkanin speak this language with ease. language-Moffic-name = Moffic language-Moffic-description = The language of the mothpeople borders on complete unintelligibility. @@ -28,8 +52,8 @@ language-Moffic-description = The language of the mothpeople borders on complete language-RobotTalk-name = RobotTalk language-RobotTalk-description = A language consisting of harsh binary chirps, whistles, hisses, and whines. Organic tongues cannot speak it without aid from special translators. -language-Sign-name = Galactic Sign Language -language-Sign-description = GSL for short, this sign language is prevalent among mute and deaf people. +language-Sign-name = Tau-Ceti Basic Sign Language +language-Sign-description = TCB-SL for short, this sign language is prevalent among mute and deaf people. language-Cat-name = Cat language-Cat-description = Meow diff --git a/Resources/Locale/en-US/loadouts/itemgroups.ftl b/Resources/Locale/en-US/loadouts/itemgroups.ftl index 29aca3ddb9b..97fcfa69848 100644 --- a/Resources/Locale/en-US/loadouts/itemgroups.ftl +++ b/Resources/Locale/en-US/loadouts/itemgroups.ftl @@ -64,3 +64,7 @@ character-item-group-LoadoutBartenderWeapon = Bartender Weapon # Service - Musician character-item-group-LoadoutMusicianInstruments = Musician Instruments + +# Traits - Languages +character-item-group-TraitsLanguagesBasic = Basic Languages +character-item-group-TraitsAccents = Accents \ No newline at end of file diff --git a/Resources/Locale/en-US/machine/machine.ftl b/Resources/Locale/en-US/machine/machine.ftl index ce8873df6f8..26059505160 100644 --- a/Resources/Locale/en-US/machine/machine.ftl +++ b/Resources/Locale/en-US/machine/machine.ftl @@ -13,6 +13,11 @@ machine-part-name-manipulator = Manipulator machine-part-name-matter-bin = Matter Bin machine-part-name-power-cell = Power Cell +upgrade-power-draw = power draw +upgrade-max-charge = max charge +upgrade-power-supply = power supply +upgrade-power-supply-ramping = power ramp rate + two-way-lever-left = push left two-way-lever-right = push right two-way-lever-cant = can't push the lever that way! diff --git a/Resources/Locale/en-US/materials/materials.ftl b/Resources/Locale/en-US/materials/materials.ftl index dca520b5b49..5a4413348e8 100644 --- a/Resources/Locale/en-US/materials/materials.ftl +++ b/Resources/Locale/en-US/materials/materials.ftl @@ -35,3 +35,6 @@ materials-raw-plasma = raw plasma materials-raw-uranium = raw uranium materials-raw-bananium = raw bananium materials-raw-salt = raw salt + +# Material Reclaimer +material-reclaimer-upgrade-process-rate = process rate diff --git a/Resources/Locale/en-US/medical/components/biomass-reclaimer-component.ftl b/Resources/Locale/en-US/medical/components/biomass-reclaimer-component.ftl index 443429c1ef3..0c0b8faf59e 100644 --- a/Resources/Locale/en-US/medical/components/biomass-reclaimer-component.ftl +++ b/Resources/Locale/en-US/medical/components/biomass-reclaimer-component.ftl @@ -1 +1,4 @@ biomass-reclaimer-suicide-others = {CAPITALIZE(THE($victim))} threw themselves into the biomass reclaimer! + +biomass-reclaimer-component-upgrade-speed = speed +biomass-reclaimer-component-upgrade-biomass-yield = biomass yield diff --git a/Resources/Locale/en-US/medical/components/cloning-pod-component.ftl b/Resources/Locale/en-US/medical/components/cloning-pod-component.ftl index b222d707a0a..e92ac86a1e4 100644 --- a/Resources/Locale/en-US/medical/components/cloning-pod-component.ftl +++ b/Resources/Locale/en-US/medical/components/cloning-pod-component.ftl @@ -1,3 +1,5 @@ cloning-pod-biomass = It currently has [color=red]{$number}[/color] units of biomass. +cloning-pod-component-upgrade-speed = cloning speed +cloning-pod-component-upgrade-biomass-requirement = biomass requirement cloning-pod-component-upgrade-emag-requirement = The card zaps something inside the cloning pod. diff --git a/Resources/Locale/en-US/medical/components/medical-scanner-component.ftl b/Resources/Locale/en-US/medical/components/medical-scanner-component.ftl index c4b19426545..da4dc7a3847 100644 --- a/Resources/Locale/en-US/medical/components/medical-scanner-component.ftl +++ b/Resources/Locale/en-US/medical/components/medical-scanner-component.ftl @@ -2,3 +2,5 @@ medical-scanner-verb-enter = Enter medical-scanner-verb-noun-occupant = occupant + +medical-scanner-upgrade-cloning = Cloning fail chance diff --git a/Resources/Locale/en-US/medical/components/stasis-bed-component.ftl b/Resources/Locale/en-US/medical/components/stasis-bed-component.ftl new file mode 100644 index 00000000000..2d8a18c263e --- /dev/null +++ b/Resources/Locale/en-US/medical/components/stasis-bed-component.ftl @@ -0,0 +1 @@ +stasis-bed-component-upgrade-stasis = stasis effect diff --git a/Resources/Locale/en-US/nutrition/components/fat-extractor.ftl b/Resources/Locale/en-US/nutrition/components/fat-extractor.ftl index 20a31cd8c40..611b8ef5406 100644 --- a/Resources/Locale/en-US/nutrition/components/fat-extractor.ftl +++ b/Resources/Locale/en-US/nutrition/components/fat-extractor.ftl @@ -1,3 +1,5 @@ +fat-extractor-component-rate = extraction rate + fat-extractor-fact-1 = Fats are triglycerides made up of a combination of different building blocks; glycerol and fatty acids. fat-extractor-fact-2 = Adults should get a recommended 20-35% of their energy intake from fat. fat-extractor-fact-3 = Being overweight or obese puts you at an increased risk of chronic diseases, such as cardiovascular diseases, metabolic syndrome, type 2 diabetes, and some types of cancers. diff --git a/Resources/Locale/en-US/psionics/psionic-powers.ftl b/Resources/Locale/en-US/psionics/psionic-powers.ftl index c68bb2a4968..01b50cce326 100644 --- a/Resources/Locale/en-US/psionics/psionic-powers.ftl +++ b/Resources/Locale/en-US/psionics/psionic-powers.ftl @@ -1,4 +1,5 @@ generic-power-initialization-feedback = I Awaken. +arleady-casting = I cannot channel more than one power at a time. # Dispel dispel-power-description = Dispel summoned entities such as familiars or forcewalls. @@ -48,6 +49,28 @@ psionic-regeneration-power-initialization-feedback = I look within myself, finding a wellspring of life. psionic-regeneration-power-metapsionic-feedback = {CAPITALIZE($entity)} possesses an overwhelming will to live +# Healing Word +action-name-healing-word = Healing Word +action-description-healing-word = Speak the Lesser Secret Of Life, and restore health to another. +healing-word-power-description = Speak the Lesser Secret Of Life, and restore health to another. +healing-word-power-initialization-feedback = + At the beginning of time, a word was spoken that brought life into the Spheres. + Though it taxes my mind to know it, this Secret is known to me now. + I need only speak it. +healing-word-power-metapsionic-feedback = {CAPITALIZE($entity)} bears the Lesser Secret of Life. +healing-word-begin = {CAPITALIZE($entity)} mutters a word that brings both joy and pain alike to those who hear it. + +# Revivify +action-name-revivify = Breath of Life +action-description-revivify = Speak the Greater Secret of Life, and restore another to life. +revivify-power-description = Speak the Greater Secret of Life, and restore another to life. +revivify-power-initialization-feedback = + For a moment, my soul journeys across time and space to the beginning of it all, there I hear it. + The Secret of Life in its fullness. I feel my entire existence burning out from within, merely by knowing it. + Power flows through me as a mighty river, begging to be released with a simple spoken word. +revivify-power-metapsionic-feedback = {CAPITALIZE($entity)} bears the Greater Secret of Life. +revivify-word-begin = {CAPITALIZE($entity)} enunciates a word of such divine power, that those who hear it weep from joy. + # Telegnosis telegnosis-power-description = Create a telegnostic projection to remotely observe things. telegnosis-power-initialization-feedback = diff --git a/Resources/Locale/en-US/shuttles/thruster.ftl b/Resources/Locale/en-US/shuttles/thruster.ftl index 94035811c73..faed6e8dd28 100644 --- a/Resources/Locale/en-US/shuttles/thruster.ftl +++ b/Resources/Locale/en-US/shuttles/thruster.ftl @@ -3,3 +3,5 @@ thruster-comp-disabled = The thruster is turned [color=red]off[/color]. thruster-comp-nozzle-direction = The nozzle is facing [color=yellow]{$direction}[/color]. thruster-comp-nozzle-exposed = The nozzle [color=green]exposed[/color] to space. thruster-comp-nozzle-not-exposed = The nozzle [color=red]is not exposed[/color] to space. + +thruster-comp-upgrade-thrust = Thrust strength diff --git a/Resources/Locale/en-US/singularity/components/emitter-component.ftl b/Resources/Locale/en-US/singularity/components/emitter-component.ftl index c71b3d6bdfd..c7db1a93bba 100644 --- a/Resources/Locale/en-US/singularity/components/emitter-component.ftl +++ b/Resources/Locale/en-US/singularity/components/emitter-component.ftl @@ -11,5 +11,8 @@ comp-emitter-turned-off = The {$target} turns off. # Shows if the user attempts to activate the emitter while it's un-anchored. comp-emitter-not-anchored = The {$target} isn't anchored to the ground! +# Upgrades +emitter-component-upgrade-fire-rate = fire rate + emitter-component-current-type = The current selected type is: {$type}. emitter-component-type-set = Type set to: {$type} diff --git a/Resources/Locale/en-US/store/sales.ftl b/Resources/Locale/en-US/store/sales.ftl new file mode 100644 index 00000000000..7223a8a0dcd --- /dev/null +++ b/Resources/Locale/en-US/store/sales.ftl @@ -0,0 +1,2 @@ +store-sales-amount = [DISCOUNT] { $amount }%! +store-sales-over = [The sale is over] diff --git a/Resources/Locale/en-US/traits/categories.ftl b/Resources/Locale/en-US/traits/categories.ftl index 56f0adeb479..2bd4b7ba49f 100644 --- a/Resources/Locale/en-US/traits/categories.ftl +++ b/Resources/Locale/en-US/traits/categories.ftl @@ -5,4 +5,7 @@ trait-category-Auditory = Auditory trait-category-Mental = Mental trait-category-Physical = Physical trait-category-Speech = Speech -trait-category-Visual = Visual +trait-category-TraitsSpeechUncategorized = Uncategorized +trait-category-TraitsSpeechAccents = Accents +trait-category-TraitsSpeechLanguages = Languages +trait-category-Visual = Visual \ No newline at end of file diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index b034f69b9e8..4db6055e46b 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -152,9 +152,30 @@ trait-description-Lethargy = trait-name-SignLanguage = Sign Language trait-description-SignLanguage = - You can understand and use Galactic Sign Language (GSL). + You can understand and use Tau-Ceti Basic Sign Language (TCB-SL). If you are mute for any reason, you can still communicate with sign language. +trait-name-SolCommon = Sol Common +trait-description-SolCommon = + With its roots in Mandarin Chinese - Common evolved as the official language of the Sol Alliance - with officials working to tie it together with a common tongue. + It's spoken by state officials - taught in schools - and spoken by those who either feel a sense of national pride in the Alliance or otherwise fell sway to the culture. + +trait-name-Tradeband = Tradeband +trait-description-Tradeband = + Descended from latin and romance languages of old Earth - Tradeband remains the main tongue of the upper class of humanity. + The language sounds elegant and well structured to most ears. It remains in popular use with traders - diplomats - and those seeking to hold onto a piece of a romantic past. + +trait-name-Freespeak = Freespeak (Gutter) +trait-description-Freespeak = + A language of renegades and frontiersmen descending from various languages from Earth like Hindi combined into a multi-rooted jumble that sounds incoherent or even barbarian to non-native speakers. + This language is the only common cultural identity for humans in the frontier. Speaking this language in itself boldly declares the speaker a free spirit. + Often called 'Gutter' by Alliance citizens. + +trait-name-Elyran = Elyran Standard +trait-description-Elyran = + Elyran Standard is the official tongue of the Republic of Elyra. + Constructed using elements of Farsi - Arabic - and Turkish - influence from all three of these languages can be seen throughout its grammar and vocabulary. + trait-name-Voracious = Voracious trait-description-Voracious = Nothing gets between you and your food. diff --git a/Resources/Locale/en-US/xenoarchaeology/traversal-distorter.ftl b/Resources/Locale/en-US/xenoarchaeology/traversal-distorter.ftl index 5c9eac57a5d..af3039e864e 100644 --- a/Resources/Locale/en-US/xenoarchaeology/traversal-distorter.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/traversal-distorter.ftl @@ -3,3 +3,5 @@ traversal-distorter-set-out = Traversal bias set to "out" traversal-distorter-desc-in = The affected artifact's traversal now favors moving inwards to the beginning. traversal-distorter-desc-out = The affected artifact's traversal now favors moving outwards towards more dangerous nodes. + +traversal-distorter-upgrade-bias = Bias effectiveness diff --git a/Resources/Locale/ru-RU/store/sales.ftl b/Resources/Locale/ru-RU/store/sales.ftl new file mode 100644 index 00000000000..93f1798fe79 --- /dev/null +++ b/Resources/Locale/ru-RU/store/sales.ftl @@ -0,0 +1,2 @@ +store-sales-amount = [СКИДКА] { $amount }%! +store-sales-over = [Скидка закончилась] diff --git a/Resources/Migrations/migration.yml b/Resources/Migrations/migration.yml index 7f19699ad8a..3b4a7178cf8 100644 --- a/Resources/Migrations/migration.yml +++ b/Resources/Migrations/migration.yml @@ -126,28 +126,6 @@ DrinkGoldschlagerGlass: DrinkGildlagerGlass ClosetBase: ClosetSteelBase MonkeyCubeBox: VariantCubeBox -# 2024-01-08 -SalvagePartsT4Spawner: SalvageLootSpawner -SalvagePartsT3Spawner: SalvageLootSpawner -SalvagePartsT3T4Spawner: SalvageLootSpawner -SalvagePartsT2Spawner: SalvageLootSpawner -AdvancedCapacitorStockPart: CapacitorStockPart -SuperCapacitorStockPart: CapacitorStockPart -QuadraticCapacitorStockPart: CapacitorStockPart -NanoManipulatorStockPart: MicroManipulatorStockPart -PicoManipulatorStockPart: MicroManipulatorStockPart -FemtoManipulatorStockPart: MicroManipulatorStockPart -AdvancedMatterBinStockPart: MatterBinStockPart -SuperMatterBinStockPart: MatterBinStockPart -BluespaceMatterBinStockPart: MatterBinStockPart -AnsibleSubspaceStockPart: null -FilterSubspaceStockPart: null -AmplifierSubspaceStockPart: null -TreatmentSubspaceStockPart: null -AnalyzerSubspaceStockPart: null -CrystalSubspaceStockPart: null -TransmitterSubspaceStockPart: null - # 2024-01-10 ClothingHeadHatHoodRad: null diff --git a/Resources/Prototypes/Actions/psionics.yml b/Resources/Prototypes/Actions/psionics.yml index 981d53884ea..d38608a469b 100644 --- a/Resources/Prototypes/Actions/psionics.yml +++ b/Resources/Prototypes/Actions/psionics.yml @@ -146,3 +146,76 @@ icon: Interface/VerbIcons/psionic_invisibility_off.png event: !type:RemovePsionicInvisibilityOffPowerActionEvent +- type: entity + id: ActionHealingWord + name: action-name-healing-word + description: action-description-healing-word + noSpawn: true + components: + - type: EntityTargetAction + icon: { sprite : Interface/Actions/psionics.rsi, state: healing_word } + useDelay: 10 + checkCanAccess: false + range: 6 + itemIconStyle: BigAction + canTargetSelf: true + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:PsionicHealOtherPowerActionEvent + healingAmount: + groups: # These all get divided by the number of damage types in the group. So they're all -2.5. + Genetic: -2.5 + Toxin: -5 + Airloss: -5 + Brute: -7.5 + Burn: -10 + rotReduction: 10 + useDelay: 1 + doRevive: true + powerName: Healing Word + popupText: healing-word-begin + playSound: true + minGlimmer: 2 + maxGlimmer: 4 + glimmerObviousSoundThreshold: 100 + glimmerObviousPopupThreshold: 200 + +- type: entity + id: ActionRevivify + name: action-name-revivify + description: action-description-revivify + noSpawn: true + components: + - type: EntityTargetAction + icon: { sprite : Interface/Actions/psionics.rsi, state: revivify } + useDelay: 120 + checkCanAccess: false + range: 2 + itemIconStyle: BigAction + canTargetSelf: false + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:PsionicHealOtherPowerActionEvent + healingAmount: + # These all get divided by the number of damage types in the group. So they're all -15 + # Additionally, they're multiplied by the caster's Amplification, which, + # assuming this is the only power they have, the multiplier is between 2.9-3.9 + groups: + Genetic: -15 + Toxin: -30 + Airloss: -60 # Except airloss, which heals 30 per type + Brute: -45 + Burn: -60 + rotReduction: 60 + doRevive: true + powerName: Revivify + popupText: revivify-begin + playSound: true + minGlimmer: 10 # These also get multiplied by caster stats. So, + maxGlimmer: 15 # keeping in mind the ~3.5x multiplier, this spikes glimmer by as much as 60 points. + glimmerObviousSoundThreshold: 50 + glimmerObviousPopupThreshold: 100 diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index a1a60e3fef0..bdd27e83a89 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -10,6 +10,7 @@ Telecrystal: 3 categories: - UplinkWeapons + saleLimit: 1 - type: listing id: UplinkRevolverPython @@ -20,6 +21,7 @@ Telecrystal: 8 # Originally was 13 TC but was not used due to high cost categories: - UplinkWeapons + saleLimit: 1 # Inbuilt suppressor so it's sneaky + more expensive. - type: listing @@ -31,6 +33,7 @@ Telecrystal: 4 categories: - UplinkWeapons + saleLimit: 1 # Poor accuracy, slow to fire, cheap option - type: listing @@ -42,6 +45,7 @@ Telecrystal: 1 categories: - UplinkWeapons + saleLimit: 1 - type: listing id: UplinkEsword @@ -53,6 +57,7 @@ Telecrystal: 8 categories: - UplinkWeapons + saleLimit: 2 - type: listing id: UplinkEnergyDagger @@ -64,6 +69,7 @@ Telecrystal: 2 categories: - UplinkWeapons + saleLimit: 1 - type: listing id: UplinkThrowingKnivesKit @@ -85,6 +91,7 @@ Telecrystal: 8 categories: - UplinkWeapons + saleLimit: 1 - type: listing id: UplinkDisposableTurret @@ -100,6 +107,7 @@ blacklist: tags: - NukeOpsUplink + saleLimit: 2 - type: listing id: BaseBallBatHomeRun @@ -112,6 +120,7 @@ Telecrystal: 16 categories: - UplinkWeapons + saleLimit: 1 # Explosives @@ -214,6 +223,7 @@ whitelist: tags: - NukeOpsUplink + saleLimit: 1 - type: listing id: UplinkC4Bundle @@ -224,6 +234,7 @@ Telecrystal: 12 #you're buying bulk so its a 25% discount categories: - UplinkExplosives + saleLimit: 1 - type: listing id: UplinkEmpGrenade @@ -261,6 +272,7 @@ blacklist: tags: - NukeOpsUplink + saleLimit: 1 - type: listing id: UplinkSyndicateBombNukie @@ -276,6 +288,7 @@ whitelist: tags: - NukeOpsUplink + saleLimit: 1 - type: listing id: UplinkClusterGrenade @@ -451,6 +464,7 @@ blacklist: tags: - NukeOpsUplink + saleLimit: 1 - type: listing id: UplinkReinforcementRadioSyndicateNukeops # Version for Nukeops that spawns an agent with the NukeOperative component. @@ -467,6 +481,7 @@ whitelist: tags: - NukeOpsUplink + saleLimit: 1 - type: listing id: UplinkReinforcementRadioSyndicateCyborgAssault @@ -483,6 +498,7 @@ whitelist: tags: - NukeOpsUplink + saleLimit: 1 - type: listing id: UplinkReinforcementRadioSyndicateMonkey @@ -515,6 +531,7 @@ whitelist: tags: - NukeOpsUplink + saleLimit: 1 - type: listing id: UplinkStealthBox @@ -545,7 +562,6 @@ productEntity: EncryptionKeyBinary cost: Telecrystal: 1 - categories: - UplinkUtility @@ -748,6 +764,7 @@ blacklist: tags: - NukeOpsUplink + saleBlacklist: true - type: listing id: UplinkDeathRattle @@ -821,6 +838,7 @@ blacklist: components: - SurplusBundle + saleLimit: 1 - type: listing id: UplinkChemistryKitBundle @@ -862,6 +880,7 @@ blacklist: components: - SurplusBundle + saleLimit: 1 - type: listing id: UplinkSniperBundle @@ -873,6 +892,7 @@ Telecrystal: 12 categories: - UplinkBundles + saleLimit: 1 - type: listing id: UplinkC20RBundle @@ -884,6 +904,7 @@ Telecrystal: 17 categories: - UplinkBundles + saleLimit: 1 - type: listing id: UplinkBulldogBundle @@ -895,6 +916,7 @@ Telecrystal: 20 categories: - UplinkBundles + saleLimit: 1 - type: listing id: UplinkGrenadeLauncherBundle @@ -906,6 +928,7 @@ Telecrystal: 25 categories: - UplinkBundles + saleLimit: 1 - type: listing id: UplinkL6SawBundle @@ -917,6 +940,7 @@ Telecrystal: 30 categories: - UplinkBundles + saleLimit: 1 - type: listing id: UplinkZombieBundle @@ -937,6 +961,7 @@ blacklist: components: - SurplusBundle + saleLimit: 1 - type: listing id: UplinkSurplusBundle @@ -956,6 +981,7 @@ blacklist: components: - SurplusBundle + saleBlacklist: true - type: listing id: UplinkSuperSurplusBundle @@ -975,6 +1001,7 @@ blacklist: components: - SurplusBundle + saleBlacklist: true # Tools @@ -1113,6 +1140,7 @@ - !type:BuyerJobCondition whitelist: - Chaplain + saleLimit: 1 - type: listing id: uplinkRevolverCapGunFake @@ -1128,6 +1156,7 @@ whitelist: - Mime - Clown + saleLimit: 1 - type: listing id: uplinkBananaPeelExplosive @@ -1172,6 +1201,7 @@ - !type:BuyerJobCondition whitelist: - Clown + saleLimit: 1 - type: listing id: uplinkHotPotato @@ -1335,6 +1365,7 @@ Telecrystal: 8 categories: - UplinkArmor + saleLimit: 1 - type: listing id: UplinkHardsuitSyndieElite @@ -1346,6 +1377,7 @@ Telecrystal: 10 categories: - UplinkArmor + saleLimit: 1 - type: listing id: UplinkClothingOuterHardsuitJuggernaut @@ -1357,6 +1389,7 @@ Telecrystal: 12 categories: - UplinkArmor + saleLimit: 1 # Misc @@ -1425,6 +1458,7 @@ whitelist: tags: - NukeOpsUplink + saleLimit: 1 - type: listing id: UplinkSoapSyndie @@ -1515,6 +1549,7 @@ Telecrystal: 12 categories: - UplinkMisc + saleLimit: 1 - type: listing id: UplinkBribe @@ -1541,6 +1576,7 @@ whitelist: tags: - NukeOpsUplink + saleLimit: 1 - type: listing id: UplinkBackpackSyndicate diff --git a/Resources/Prototypes/CharacterItemGroups/languageGroups.yml b/Resources/Prototypes/CharacterItemGroups/languageGroups.yml new file mode 100644 index 00000000000..244792e4c67 --- /dev/null +++ b/Resources/Prototypes/CharacterItemGroups/languageGroups.yml @@ -0,0 +1,31 @@ +- type: characterItemGroup + id: TraitsLanguagesBasic + maxItems: 1 + items: + - type: trait + id: SignLanguage + - type: trait + id: SolCommon + - type: trait + id: Tradeband + - type: trait + id: Freespeak + - type: trait + id: Elyran + +- type: characterItemGroup + id: TraitsAccents + maxItems: 1 + items: + - type: trait + id: FrontalLisp + - type: trait + id: Stutter + - type: trait + id: PirateAccent + - type: trait + id: Accentless + - type: trait + id: Southern + - type: trait + id: ScottishAccent \ No newline at end of file diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/animals.yml index dd59d74d3f0..de0f022e1f7 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/animals.yml @@ -184,4 +184,4 @@ - Dog understands: - Dog - - GalacticCommon + - TauCetiBasic diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml index 364907ff9c4..4d3d139e8e0 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml @@ -111,9 +111,9 @@ - NanoTrasen - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic understands: - - GalacticCommon + - TauCetiBasic - type: GhostTakeoverAvailable - type: GhostRole makeSentient: true diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/nukiemouse.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/nukiemouse.yml index 8968f0e77ad..59c7fe2b212 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/nukiemouse.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/nukiemouse.yml @@ -101,7 +101,7 @@ - Mouse understands: - Mouse - - GalacticCommon + - TauCetiBasic - type: Tag tags: - VimPilot diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml index 07152d74ef0..b3027839b1a 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml @@ -106,10 +106,10 @@ - type: Wagging - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - Canilunzt understands: - - GalacticCommon + - TauCetiBasic - Canilunzt - type: ConsumeDelayModifier foodDelayMultiplier: 0.5 diff --git a/Resources/Prototypes/DeltaV/Traits/neutral.yml b/Resources/Prototypes/DeltaV/Traits/neutral.yml index 6168d7045a9..469fa5b34b7 100644 --- a/Resources/Prototypes/DeltaV/Traits/neutral.yml +++ b/Resources/Prototypes/DeltaV/Traits/neutral.yml @@ -1,6 +1,9 @@ - type: trait id: ScottishAccent - category: Speech + category: TraitsSpeechAccents points: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: TraitsAccents components: - type: ScottishAccent diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/salvage.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/salvage.yml index 34bf32d8d7b..4beffbc9c22 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/salvage.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/salvage.yml @@ -63,23 +63,78 @@ offset: 0.0 - type: entity - name: salvage loot spawner - id: SalvageLootSpawner + name: Salvage T2 Machine Parts Spawner + id: SalvagePartsT2Spawner parent: MarkerBase components: - type: Sprite layers: - state: red - - sprite: Objects/Weapons/Melee/crusher.rsi - state: icon + - sprite: Objects/Misc/stock_parts.rsi + state: advanced_matter_bin - type: RandomSpawner prototypes: - - WeaponCrusher - - WeaponCrusherDagger - - WeaponCrusherGlaive - - MiningDrill + - AdvancedCapacitorStockPart + - NanoManipulatorStockPart + - AdvancedMatterBinStockPart offset: 0.0 +- type: entity + parent: MarkerBase + id: SalvagePartsT3T4Spawner + name: tier 3/4 machine part + components: + - type: Sprite + layers: + - sprite: Objects/Misc/stock_parts.rsi + state: super_matter_bin + - type: RandomSpawner + rarePrototypes: + - QuadraticCapacitorStockPart + - FemtoManipulatorStockPart + - BluespaceMatterBinStockPart + rareChance: 0.05 + prototypes: + - SuperCapacitorStockPart + - PicoManipulatorStockPart + - SuperMatterBinStockPart + chance: 0.95 + offset: 0.0 + +- type: entity + parent: MarkerBase + id: SalvagePartsT3Spawner + name: tier 3 machine part + suffix: Spawner + components: + - type: Sprite + layers: + - sprite: Objects/Misc/stock_parts.rsi + state: super_matter_bin + - type: RandomSpawner + prototypes: + - SuperCapacitorStockPart + - PicoManipulatorStockPart + - SuperMatterBinStockPart + offset: 0.0 + +- type: entity + parent: MarkerBase + id: SalvagePartsT4Spawner + name: tier 4 machine part + suffix: Spawner + components: + - type: Sprite + layers: + - sprite: Objects/Misc/stock_parts.rsi + state: bluespace_matter_bin + - type: RandomSpawner + prototypes: + - QuadraticCapacitorStockPart + - PicoManipulatorStockPart + - BluespaceMatterBinStockPart + offset: 0.0 + - type: entity name: Salvage Mob Spawner id: SalvageMobSpawner @@ -228,3 +283,25 @@ - MobFleshLoverSalvage chance: 1 offset: 0.2 + +- type: entity + name: Salvage Loot Spawner + id: SalvageLootSpawner + parent: MarkerBase + components: + - type: Sprite + layers: + - state: red + - sprite: Structures/Storage/Crates/generic.rsi + state: icon + - type: RandomSpawner + rarePrototypes: + - SalvagePartsT2Spawner + - SalvagePartsT3Spawner + - SalvagePartsT3T4Spawner + - SalvagePartsT4Spawner + rareChance: 0.4 + prototypes: + - CrateSalvageAssortedGoodies + chance: 0.9 + offset: 0.2 diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 6340cb888b6..81bd0332a58 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -218,10 +218,10 @@ - Normal - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - RobotTalk understands: - - GalacticCommon + - TauCetiBasic - RobotTalk - type: PsionicInsulation diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 01eed043139..343a39ca70f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1370,7 +1370,7 @@ understands: - Monkey - Kobold - - GalacticCommon + - TauCetiBasic - type: NpcFactionMember factions: - Syndicate @@ -2002,9 +2002,9 @@ bloodMaxVolume: 50 - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic understands: - - GalacticCommon + - TauCetiBasic - type: entity name: penguin @@ -2903,7 +2903,7 @@ - Cat understands: - Cat - - GalacticCommon + - TauCetiBasic - type: entity name: space cat @@ -3450,7 +3450,7 @@ speaks: - RootSpeak understands: - - GalacticCommon + - TauCetiBasic - RootSpeak - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/dogs.yml b/Resources/Prototypes/Entities/Mobs/NPCs/dogs.yml index 22da7583723..2bcd3776260 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/dogs.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/dogs.yml @@ -89,7 +89,7 @@ - Dog understands: - Dog - - GalacticCommon + - TauCetiBasic - type: entity parent: MobPibble diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml index 6eb43fb89ae..4ec4ee6a12f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml @@ -40,7 +40,7 @@ speaks: - Dog understands: - - GalacticCommon + - TauCetiBasic - Dog - type: entity @@ -131,7 +131,7 @@ speaks: - Cat understands: - - GalacticCommon + - TauCetiBasic - Cat - type: entity @@ -155,7 +155,7 @@ speaks: - Cat understands: - - GalacticCommon + - TauCetiBasic - Cat - type: entity @@ -310,7 +310,7 @@ speaks: - Dog understands: - - GalacticCommon + - TauCetiBasic - Dog - type: InteractionPopup successChance: 0.5 @@ -415,7 +415,7 @@ speaks: - Dog understands: - - GalacticCommon + - TauCetiBasic - Dog - type: InteractionPopup successChance: 0.7 @@ -580,7 +580,7 @@ speaks: - Fox understands: - - GalacticCommon + - TauCetiBasic - Fox - type: entity @@ -633,7 +633,7 @@ speaks: - Mouse understands: - - GalacticCommon + - TauCetiBasic - Mouse - type: entity @@ -811,7 +811,7 @@ speaks: - Bubblish understands: - - GalacticCommon + - TauCetiBasic - Bubblish - type: entity @@ -851,7 +851,7 @@ speaks: - Monkey understands: - - GalacticCommon + - TauCetiBasic - Monkey - Kobold @@ -885,5 +885,5 @@ speaks: - Crab understands: - - GalacticCommon + - TauCetiBasic - Crab diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml index cf563989bf5..e1ea11cc2c3 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml @@ -120,10 +120,10 @@ gender: male - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - Mouse understands: - - GalacticCommon + - TauCetiBasic - Mouse - type: entity @@ -301,7 +301,7 @@ speaks: - Mouse understands: - - GalacticCommon + - TauCetiBasic - Mouse - type: FireVisuals sprite: Mobs/Effects/onfire.rsi diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index 90d9a5e5c97..0e279a07eb6 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -108,10 +108,10 @@ - type: ZombieImmune - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - RobotTalk understands: - - GalacticCommon + - TauCetiBasic - RobotTalk - type: PsionicInsulation diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index d618e407134..fc0f879b1ce 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -239,10 +239,10 @@ - CannotSuicide - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - Xeno understands: - - GalacticCommon + - TauCetiBasic - Xeno - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/Player/ipc.yml b/Resources/Prototypes/Entities/Mobs/Player/ipc.yml index 247226dc7d5..0e1e1a1ae7d 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/ipc.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/ipc.yml @@ -101,10 +101,10 @@ - type: CanHostGuardian - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - RobotTalk understands: - - GalacticCommon + - TauCetiBasic - RobotTalk - type: WeldingHealable - type: PsionicInsulation diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 33635eeec20..7420f168ca6 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -218,9 +218,9 @@ - type: CanEscapeInventory # Carrying system from nyanotrasen. - type: LanguageKnowledge # This is here so even if species doesn't have a defined language, they at least speak GC speaks: - - GalacticCommon + - TauCetiBasic understands: - - GalacticCommon + - TauCetiBasic - type: Tag tags: - CanPilot diff --git a/Resources/Prototypes/Entities/Mobs/Species/diona.yml b/Resources/Prototypes/Entities/Mobs/Species/diona.yml index 81c9e596163..9a26d0ec909 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/diona.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/diona.yml @@ -104,10 +104,10 @@ - Dead - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - RootSpeak understands: - - GalacticCommon + - TauCetiBasic - RootSpeak - type: TraitSpeedModifier sprintModifier: 0.75 diff --git a/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml b/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml index 055c6522ddc..4b75e65ca91 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml @@ -53,10 +53,10 @@ speechSounds: Bass - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - SolCommon understands: - - GalacticCommon + - TauCetiBasic - SolCommon - type: LightweightDrunk boozeStrengthMultiplier: 0.5 diff --git a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml index 8882da868b1..8d657d3532f 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml @@ -117,11 +117,11 @@ - ShoesRequiredStepTriggerImmune - type: LanguageKnowledge speaks: - - GalacticCommon - - SolCommon + - TauCetiBasic + - Tradeband understands: - - GalacticCommon - - SolCommon + - TauCetiBasic + - Tradeband - type: StepTriggerImmune whitelist: types: diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index ac373725ce4..121cbd3ca78 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -18,10 +18,10 @@ amount: 5 - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - SolCommon understands: - - GalacticCommon + - TauCetiBasic - SolCommon - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/Species/moth.yml b/Resources/Prototypes/Entities/Mobs/Species/moth.yml index 1c55dcf0df1..7e5716a212f 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/moth.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/moth.yml @@ -25,10 +25,10 @@ speechVerb: Moth - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - Moffic understands: - - GalacticCommon + - TauCetiBasic - Moffic - type: TypingIndicator proto: moth diff --git a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml index 35f9e9fa393..3dfb5cb3267 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml @@ -61,10 +61,10 @@ - type: Wagging - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - Draconic understands: - - GalacticCommon + - TauCetiBasic - Draconic - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index 0e05b0c8276..2982cdb5b27 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -76,10 +76,10 @@ maxSaturation: 15 - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - Bubblish understands: - - GalacticCommon + - TauCetiBasic - Bubblish - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml index 93d4b957fe7..743d9a17925 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks.yml @@ -48,6 +48,31 @@ path: /Audio/SimpleStation14/Items/Handling/drinkglass_drop.ogg params: volume: -2 + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.25,0.25,0.25" + density: 30 + mask: + - ItemMask + layer: + - BulletImpassable # Ever seen a John Woo movie? + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 5 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + params: + volume: -6 + - !type:SpillBehavior { } + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity parent: DrinkBase diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml index a6752286dd2..b2489325e0a 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_bottles.yml @@ -31,6 +31,9 @@ !type:DamageTrigger damage: 5 behaviors: + - !type:PlaySoundBehavior + sound: + collection: GlassBreak - !type:SpillBehavior { } - !type:DoActsBehavior acts: [ "Destruction" ] diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/plate.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/plate.yml index 63c47df67e7..3bd66e8a481 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/plate.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/plate.yml @@ -54,6 +54,17 @@ materialComposition: Glass: 60 - type: SpaceGarbage + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.25,0.25,0.25" + density: 25 + mask: + - ItemMask + layer: + - BulletImpassable # Ever seen a John Woo movie? - type: entity name: broken plate diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml index 1210f302fcc..35a941e759c 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/computer.yml @@ -21,8 +21,8 @@ - type: entity parent: BaseComputerCircuitboard id: AlertsComputerCircuitboard - name: alerts computer board - description: A computer printed circuit board for an alerts computer. + name: atmospheric alerts computer board + description: A computer printed circuit board for an atmospheric alerts computer. components: - type: ComputerBoard prototype: ComputerAlert diff --git a/Resources/Prototypes/Entities/Objects/Devices/translator_implants.yml b/Resources/Prototypes/Entities/Objects/Devices/translator_implants.yml index da42b2774b1..d430f5ea5dc 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/translator_implants.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/translator_implants.yml @@ -1,26 +1,26 @@ - type: entity parent: BaseSubdermalImplant - id: BasicGalacticCommonTranslatorImplant + id: BasicTauCetiBasicTranslatorImplant name: basic common translator implant description: Provides your illiterate friends the ability to understand the common galactic tongue. noSpawn: true components: - type: TranslatorImplant understood: - - GalacticCommon + - TauCetiBasic - type: entity parent: BaseSubdermalImplant - id: GalacticCommonTranslatorImplant + id: TauCetiBasicTranslatorImplant name: advanced common translator implant description: A more advanced version of the translator implant, teaches your illiterate friends the ability to both speak and understand the galactic tongue! noSpawn: true components: - type: TranslatorImplant understood: - - GalacticCommon + - TauCetiBasic spoken: - - GalacticCommon + - TauCetiBasic - type: entity parent: BaseSubdermalImplant @@ -35,7 +35,7 @@ spoken: - Bubblish requires: - - GalacticCommon + - TauCetiBasic - type: entity parent: BaseSubdermalImplant @@ -50,7 +50,7 @@ spoken: - Nekomimetic requires: - - GalacticCommon + - TauCetiBasic - type: entity parent: BaseSubdermalImplant @@ -65,7 +65,7 @@ spoken: - Draconic requires: - - GalacticCommon + - TauCetiBasic - type: entity parent: BaseSubdermalImplant @@ -80,7 +80,7 @@ spoken: - Canilunzt requires: - - GalacticCommon + - TauCetiBasic - type: entity parent: BaseSubdermalImplant @@ -95,7 +95,7 @@ spoken: - SolCommon requires: - - GalacticCommon + - TauCetiBasic - type: entity parent: BaseSubdermalImplant @@ -110,7 +110,7 @@ spoken: - RootSpeak requires: - - GalacticCommon + - TauCetiBasic - type: entity parent: BaseSubdermalImplant @@ -125,4 +125,4 @@ spoken: - Moffic requires: - - GalacticCommon + - TauCetiBasic diff --git a/Resources/Prototypes/Entities/Objects/Devices/translators.yml b/Resources/Prototypes/Entities/Objects/Devices/translators.yml index 6aa7947c82d..4a14e371565 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/translators.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/translators.yml @@ -67,13 +67,13 @@ components: - type: HandheldTranslator spoken: - - GalacticCommon + - TauCetiBasic - Canilunzt understood: - - GalacticCommon + - TauCetiBasic - Canilunzt requires: - - GalacticCommon + - TauCetiBasic - Canilunzt - type: entity @@ -84,13 +84,13 @@ components: - type: HandheldTranslator spoken: - - GalacticCommon + - TauCetiBasic - Bubblish understood: - - GalacticCommon + - TauCetiBasic - Bubblish requires: - - GalacticCommon + - TauCetiBasic - Bubblish - type: entity @@ -101,13 +101,13 @@ components: - type: HandheldTranslator spoken: - - GalacticCommon + - TauCetiBasic - Nekomimetic understood: - - GalacticCommon + - TauCetiBasic - Nekomimetic requires: - - GalacticCommon + - TauCetiBasic - Nekomimetic - type: entity @@ -118,13 +118,13 @@ components: - type: HandheldTranslator spoken: - - GalacticCommon + - TauCetiBasic - Draconic understood: - - GalacticCommon + - TauCetiBasic - Draconic requires: - - GalacticCommon + - TauCetiBasic - Draconic - type: entity @@ -135,13 +135,13 @@ components: - type: HandheldTranslator spoken: - - GalacticCommon + - TauCetiBasic - SolCommon understood: - - GalacticCommon + - TauCetiBasic - SolCommon requires: - - GalacticCommon + - TauCetiBasic - SolCommon - type: entity @@ -152,13 +152,13 @@ components: - type: HandheldTranslator spoken: - - GalacticCommon + - TauCetiBasic - RootSpeak understood: - - GalacticCommon + - TauCetiBasic - RootSpeak requires: - - GalacticCommon + - TauCetiBasic - RootSpeak - type: entity @@ -169,13 +169,13 @@ components: - type: HandheldTranslator spoken: - - GalacticCommon + - TauCetiBasic - Moffic understood: - - GalacticCommon + - TauCetiBasic - Moffic requires: - - GalacticCommon + - TauCetiBasic - Moffic - type: entity @@ -186,13 +186,13 @@ components: - type: HandheldTranslator spoken: - - GalacticCommon + - TauCetiBasic - Xeno understood: - - GalacticCommon + - TauCetiBasic - Xeno requires: - - GalacticCommon + - TauCetiBasic - type: entity id: AnimalTranslator @@ -217,5 +217,5 @@ - Kobold - Hissing requires: - - GalacticCommon + - TauCetiBasic setLanguageOnInteract: false diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml index 947a973bbf6..730d532930b 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml @@ -25,7 +25,7 @@ bluntStaminaDamageFactor: 1.5 heavyRateModifier: 0.75 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 angle: 75 - type: Item size: Normal @@ -67,7 +67,7 @@ bluntStaminaDamageFactor: 1.5 heavyRateModifier: 0.75 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 angle: 75 - type: Item size: Normal @@ -110,7 +110,7 @@ bluntStaminaDamageFactor: 1.5 heavyRateModifier: 0.75 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 15 + heavyStaminaCost: 10 angle: 160 - type: Wieldable - type: IncreaseDamageOnWield @@ -234,7 +234,7 @@ bluntStaminaDamageFactor: 2 heavyRateModifier: 0.75 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 angle: 75 - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index 9e9740a0a82..d7a5f3542dc 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -630,12 +630,11 @@ state: icon - type: MeleeWeapon attackRate: 1.5 - range: 1.3 + range: 1.5 damage: types: Blunt: 0.1 heavyDamageBaseModifier: 2 - heavyStaminaCost: 5 maxTargets: 8 angle: 25 - type: Clothing diff --git a/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml b/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml index f8dbabd07a1..a6cbe9a6e7e 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml @@ -6,8 +6,8 @@ components: - type: Sharp - type: MeleeWeapon - attackRate: 1.5 - range: 1.3 + attackRate: 1.4 + range: 1.4 damage: types: Slash: 4 diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index f1802e426fb..27a81a783a6 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -46,8 +46,8 @@ Blunt: 8 heavyRateModifier: 0.8 heavyDamageBaseModifier: 2 - heavyStaminaCost: 15 - maxTargets: 8 + heavyStaminaCost: 7.5 + maxTargets: 6 soundHit: path: /Audio/Weapons/smash.ogg - type: Tool diff --git a/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml b/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml index 341acb52f0b..d90b03528e5 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml @@ -16,6 +16,10 @@ sound: /Audio/SimpleStation14/Items/Handling/component_drop.ogg - type: EmitSoundOnLand sound: /Audio/SimpleStation14/Items/Handling/component_drop.ogg +# Rating 1 + - type: GuideHelp + guides: + - MachineUpgrading # Rating 1 @@ -69,3 +73,218 @@ - type: ReverseEngineering # Nyano recipes: - MatterBinStockPart + +# Rating 2 + +- type: entity + id: AdvancedCapacitorStockPart + name: advanced capacitor + parent: CapacitorStockPart + description: An advanced capacitor used in the construction of a variety of devices. + suffix: Rating 2 + components: + - type: Sprite + state: adv_capacitor + - type: MachinePart + rating: 2 + - type: ReverseEngineering # Nyano + difficulty: 2 + recipes: + - AdvancedCapacitorStockPart + +- type: entity + id: NanoManipulatorStockPart + name: advanced manipulator + parent: MicroManipulatorStockPart + description: An advanced manipulator used in the construction of a variety of devices. + suffix: Rating 2 + components: + - type: Sprite + state: nano_mani + - type: MachinePart + rating: 2 + - type: ReverseEngineering # Nyano + difficulty: 2 + recipes: + - NanoManipulatorStockPart + +- type: entity + id: AdvancedMatterBinStockPart + name: advanced matter bin + parent: MatterBinStockPart + description: An advanced matter bin used in the construction of a variety of devices. + suffix: Rating 2 + components: + - type: Sprite + state: advanced_matter_bin + - type: MachinePart + rating: 2 + - type: ReverseEngineering # Nyano + difficulty: 2 + recipes: + - AdvancedMatterBinStockPart + +# Rating 3 + +- type: entity + id: SuperCapacitorStockPart + name: super capacitor + parent: CapacitorStockPart + description: A super capacitor used in the construction of a variety of devices. + suffix: Rating 3 + components: + - type: Sprite + state: super_capacitor + - type: MachinePart + rating: 3 + - type: ReverseEngineering # Nyano + difficulty: 3 + recipes: + - SuperCapacitorStockPart + +- type: entity + id: PicoManipulatorStockPart + name: super manipulator + parent: MicroManipulatorStockPart + description: A super manipulator used in the construction of a variety of devices. + suffix: Rating 3 + components: + - type: Sprite + state: pico_mani + - type: MachinePart + rating: 3 + - type: ReverseEngineering # Nyano + difficulty: 3 + recipes: + - PicoManipulatorStockPart + +- type: entity + id: SuperMatterBinStockPart + name: super matter bin + parent: MatterBinStockPart + description: A super matter bin used in the construction of a variety of devices. + suffix: Rating 3 + components: + - type: Sprite + state: super_matter_bin + - type: MachinePart + rating: 3 + - type: ReverseEngineering # Nyano + difficulty: 3 + recipes: + - SuperMatterBinStockPart + +# Rating 4 + +- type: entity + id: QuadraticCapacitorStockPart + name: bluespace capacitor + parent: CapacitorStockPart + description: A bluespace capacitor used in the construction of a variety of devices. + suffix: Rating 4 + components: + - type: Sprite + state: quadratic_capacitor + - type: MachinePart + rating: 4 + - type: ReverseEngineering # Nyano + difficulty: 4 + recipes: + - QuadraticCapacitorStockPart + +- type: entity + id: FemtoManipulatorStockPart + name: bluespace manipulator + parent: MicroManipulatorStockPart + description: A bluespace manipulator used in the construction of a variety of devices. + suffix: Rating 4 + components: + - type: Sprite + state: femto_mani + - type: MachinePart + rating: 4 + - type: ReverseEngineering # Nyano + difficulty: 4 + recipes: + - FemtoManipulatorStockPart + +- type: entity + id: BluespaceMatterBinStockPart + name: bluespace matter bin + parent: MatterBinStockPart + description: A bluespace matter bin used in the construction of a variety of devices. + suffix: Rating 4 + components: + - type: Sprite + state: bluespace_matter_bin + - type: MachinePart + rating: 4 + - type: ReverseEngineering # Nyano + difficulty: 4 + recipes: + - BluespaceMatterBinStockPart + +# Subspace stock parts (REMOVE THESE) + +- type: entity + id: AnsibleSubspaceStockPart + name: subspace ansible + parent: BaseStockPart + description: A compact module capable of sensing extradimensional activity. + components: + - type: Sprite + state: subspace_ansible + +- type: entity + id: FilterSubspaceStockPart + name: hyperwave filter + parent: BaseStockPart + description: A tiny device capable of filtering and converting super-intense radiowaves. + components: + - type: Sprite + state: hyperwave_filter + +- type: entity + id: AmplifierSubspaceStockPart + name: subspace amplifier + parent: BaseStockPart + description: A compact micro-machine capable of amplifying weak subspace transmissions. + components: + - type: Sprite + state: subspace_amplifier + +- type: entity + id: TreatmentSubspaceStockPart + name: subspace treatment disk + parent: BaseStockPart + description: A compact micro-machine capable of stretching out hyper-compressed radio waves. + components: + - type: Sprite + state: treatment_disk + +- type: entity + id: AnalyzerSubspaceStockPart + name: subspace wavelength analyzer + parent: BaseStockPart + description: A sophisticated analyzer capable of analyzing cryptic subspace wavelengths. + components: + - type: Sprite + state: wavelength_analyzer + +- type: entity + id: CrystalSubspaceStockPart + name: ansible crystal + parent: BaseStockPart + description: A crystal made from pure glass used to transmit laser databursts to subspace. + components: + - type: Sprite + state: ansible_crystal + +- type: entity + id: TransmitterSubspaceStockPart + name: subspace transmitter + parent: BaseStockPart + description: A large piece of equipment used to open a window into the subspace dimension. + components: + - type: Sprite + state: subspace_transmitter diff --git a/Resources/Prototypes/Entities/Objects/Misc/translator_implanters.yml b/Resources/Prototypes/Entities/Objects/Misc/translator_implanters.yml index 8b5b262ff8a..92e21e51404 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/translator_implanters.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/translator_implanters.yml @@ -10,7 +10,7 @@ name: basic common translator implanter components: - type: Implanter - implant: BasicGalacticCommonTranslatorImplant + implant: BasicTauCetiBasicTranslatorImplant - type: entity id: AdvancedGalaticCommonTranslatorImplanter @@ -18,7 +18,7 @@ name: advanced common translator implanter components: - type: Implanter - implant: GalacticCommonTranslatorImplant + implant: TauCetiBasicTranslatorImplant - type: entity id: BubblishTranslatorImplanter diff --git a/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml b/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml index 9ab53cebc96..50bea2a8b24 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml @@ -50,7 +50,7 @@ heavyRateModifier: 0.8 heavyDamageBaseModifier: 1 heavyStaminaCost: 5 - maxTargets: 3 + maxTargets: 4 - type: Tag tags: - Book diff --git a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml index 37b8daddc27..c43cce1f8b1 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml @@ -21,7 +21,6 @@ heavyRateModifier: 1 heavyRangeModifier: 1 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 5 maxTargets: 5 angle: 100 - type: Item @@ -48,7 +47,6 @@ heavyRateModifier: 0.9 heavyRangeModifier: 1.25 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 5 maxTargets: 1 angle: 20 - type: Item @@ -103,7 +101,7 @@ wideAnimationRotation: 135 swingLeft: true attackRate: 1.25 - range: 1.25 + range: 1.4 damage: types: Slash: 10 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index de5c33671a8..f45331d5897 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -16,7 +16,7 @@ heavyRateModifier: 0.8 heavyRangeModifier: 1.25 heavyDamageBaseModifier: 1.25 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 maxTargets: 2 angle: 180 soundHit: @@ -64,7 +64,7 @@ heavyRateModifier: 0.8 heavyRangeModifier: 1.25 heavyDamageBaseModifier: 1.25 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 maxTargets: 2 angle: 180 soundHit: diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml index c81768da4d3..027d53f5bcd 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml @@ -51,7 +51,7 @@ - 1,1,1,1 - type: MeleeWeapon attackRate: 0.75 - range: 1.3 + range: 1.4 damage: types: Piercing: 8 @@ -87,7 +87,7 @@ wideAnimationRotation: 90 swingLeft: true attackRate: 1.25 - range: 1.25 + range: 1.4 damage: types: Slash: 7.5 @@ -208,7 +208,8 @@ heavyStaminaCost: 20 maxTargets: 8 angle: 20 -# ~~No melee for regular saw because have you ever seen someone use a band saw as a weapon? It's dumb.~~ No, I'm going to saw through your bones. +# --No melee for regular saw because have you ever seen someone use a band saw as a weapon? It's dumb.-- +# No, I'm going to saw through your bones. - type: entity name: choppa @@ -252,7 +253,7 @@ storedRotation: 90 - type: MeleeWeapon attackRate: 1.15 - range: 1.4 + range: 1.5 bluntStaminaDamageFactor: 3.0 damage: types: @@ -260,7 +261,7 @@ Slash: 5.5 heavyRateModifier: 0.5 heavyDamageBaseModifier: 1 - heavyStaminaCost: 15 + heavyStaminaCost: 10 maxTargets: 8 angle: 360 soundHit: @@ -282,8 +283,8 @@ heldPrefix: advanced storedRotation: 90 - type: MeleeWeapon - attackRate: 1.25 - range: 1.4 + attackRate: 1.15 + range: 1.5 bluntStaminaDamageFactor: 5.0 damage: types: @@ -291,7 +292,7 @@ Slash: 7.5 heavyRateModifier: 0.5 heavyDamageBaseModifier: 1 - heavyStaminaCost: 15 + heavyStaminaCost: 10 maxTargets: 8 angle: 360 soundHit: diff --git a/Resources/Prototypes/Entities/Objects/Specific/Research/rped.yml b/Resources/Prototypes/Entities/Objects/Specific/Research/rped.yml index 84a51b4f345..fca8c8ae85f 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Research/rped.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Research/rped.yml @@ -10,6 +10,9 @@ - type: Item sprite: Objects/Specific/Research/rped.rsi size: Normal + - type: GuideHelp + guides: + - MachineUpgrading - type: PartExchanger - type: Storage grid: diff --git a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml index 2b75a7e3dd9..2a982ed6f79 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml @@ -120,9 +120,8 @@ damage: types: Blunt: 6.5 - bluntStaminaDamageFactor: 1.5 + bluntStaminaDamageFactor: 2 heavyRateModifier: 0.9 - heavyStaminaCost: 5 maxTargets: 1 angle: 20 soundHit: diff --git a/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml b/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml index f739de251cb..2618666ed9a 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml @@ -41,9 +41,9 @@ bluntStaminaDamageFactor: 2.5 heavyRateModifier: 0.8 heavyDamageBaseModifier: 1.5 - heavyStaminaCost: 15 - maxTargets: 1 - angle: 140 + heavyStaminaCost: 10 + maxTargets: 3 + angle: 100 - type: PhysicalComposition materialComposition: Steel: 185 diff --git a/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml b/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml index 12521ff644f..936e832224f 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml @@ -44,8 +44,8 @@ changeSound: /Audio/Items/change_jaws.ogg - type: MeleeWeapon wideAnimationRotation: 90 - attackRate: 0.75 - range: 1.75 + attackRate: 0.85 + range: 1.65 damage: types: Blunt: 10 @@ -53,7 +53,7 @@ bluntStaminaDamageFactor: 2.0 heavyRateModifier: 0.8 heavyDamageBaseModifier: 1.5 - heavyStaminaCost: 10 + heavyStaminaCost: 5 maxTargets: 1 angle: 20 soundHit: diff --git a/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml b/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml index 6702ae39d69..b242843634d 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml @@ -28,7 +28,7 @@ Blunt: 9 bluntStaminaDamageFactor: 2.0 heavyRateModifier: 0.8 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 angle: 80.5 soundHit: path: "/Audio/Weapons/smash.ogg" diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index c0d4fa179f2..87d0c2f4003 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -38,9 +38,7 @@ Blunt: 6.5 heavyRateModifier: 0.9 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 5 maxTargets: 4 - angle: 60 soundHit: path: "/Audio/Items/wirecutter.ogg" - type: Tool @@ -103,7 +101,6 @@ Piercing: 6 heavyRateModifier: 0.75 heavyDamageBaseModifier: 1.5 - heavyStaminaCost: 5 maxTargets: 1 angle: 20 soundHit: @@ -165,7 +162,6 @@ bluntStaminaDamageFactor: 1.5 heavyRateModifier: 0.75 heavyDamageBaseModifier: 1.75 - heavyStaminaCost: 5 angle: 100 soundHit: collection: MetalThud @@ -279,7 +275,6 @@ Shock: 2 heavyRateModifier: 0.9 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 5 maxTargets: 1 angle: 20 - type: Item @@ -424,7 +419,6 @@ Piercing: 8 heavyRateModifier: 0.9 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 5 maxTargets: 1 angle: 20 soundHit: @@ -641,14 +635,14 @@ - type: MeleeWeapon wideAnimationRotation: 45 attackRate: 0.8 - range: 2.0 + range: 1.75 damage: types: Blunt: 8 bluntStaminaDamageFactor: 1.5 heavyRateModifier: 0.9 heavyDamageBaseModifier: 1.5 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 angle: 100 soundHit: collection: MetalThud @@ -696,8 +690,7 @@ bluntStaminaDamageFactor: 2.0 heavyRateModifier: 0.8 heavyDamageBaseModifier: 1.5 - heavyStaminaCost: 5 - maxTargets: 1 + maxTargets: 2 angle: 20 soundHit: collection: MetalThud diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml index 49b2eeaada1..a721f254c89 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml @@ -14,6 +14,7 @@ slots: - Back - type: Wieldable + - type: GunRequiresWield - type: GunWieldBonus minAngle: -20 maxAngle: -20 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml index c55b2b6b091..2e7265a2c77 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml @@ -14,6 +14,8 @@ slots: - Back - suitStorage + - type: Wieldable + - type: GunRequiresWield - type: AmmoCounter - type: Gun fireRate: 5 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml index 52b05b6d60b..d1635f49791 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml @@ -77,6 +77,8 @@ soundEmpty: path: /Audio/Weapons/Guns/Empty/empty.ogg fireOnDropChance: 0.3 + - type: Wieldable + - type: GunRequiresWield - type: ItemSlots slots: gun_magazine: @@ -118,6 +120,8 @@ - type: Gun fireRate: 2 fireOnDropChance: 0.5 + - type: Wieldable + - type: GunRequiresWield - type: BallisticAmmoProvider capacity: 2 - type: Construction @@ -146,6 +150,8 @@ - type: Clothing sprite: DeltaV/Objects/Weapons/Guns/Shotguns/enforcer.rsi # Delta-V - type: BallisticAmmoProvider + - type: Wieldable + - type: GunRequiresWield - type: entity parent: WeaponShotgunEnforcer @@ -176,6 +182,8 @@ - type: Tag tags: - WeaponShotgunKammerer + - type: Wieldable + - type: GunRequiresWield - type: entity name: sawn-off shotgun @@ -260,6 +268,8 @@ capacity: 1 - type: StaticPrice price: 0 + - type: Wieldable + - type: GunRequiresWield - type: entity name: improvised shotgun diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml index c85314e91ca..c4f7a8cd15b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml @@ -18,6 +18,8 @@ - Back - suitStorage - type: AmmoCounter + - type: Wieldable + - type: GunRequiresWield - type: Gun fireRate: 0.75 selectedMode: SemiAuto diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml index 8780d377e05..350ea94bd9f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml @@ -17,7 +17,7 @@ bluntStaminaDamageFactor: 2.0 heavyRateModifier: 0.5 heavyDamageBaseModifier: 1.75 - heavyStaminaCost: 15 + heavyStaminaCost: 10 maxTargets: 2 angle: 120 soundHit: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml index 5e9d789b658..a681ef52ebf 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml @@ -11,7 +11,7 @@ - type: MeleeWeapon wideAnimationRotation: -135 attackRate: 1.25 - range: 1.4 + range: 1.5 damage: types: Slash: 8 @@ -39,12 +39,12 @@ - type: MeleeWeapon wideAnimationRotation: -135 attackRate: 0.75 - range: 1.75 + range: 1.65 damage: types: Slash: 12 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 maxTargets: 6 angle: 90 - type: Item @@ -70,7 +70,7 @@ state: icon - type: MeleeWeapon wideAnimationRotation: -135 - attackRate: 0.75 + attackRate: 0.85 range: 1.75 damage: types: @@ -79,7 +79,7 @@ Structural: 5 heavyRateModifier: 0.9 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 angle: 100 soundHit: collection: MetalThud diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/home_run_bat.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/home_run_bat.yml index 5cb5795c8a5..43a4fb6c59b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/home_run_bat.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/home_run_bat.yml @@ -14,8 +14,7 @@ bluntStaminaDamageFactor: 2.0 heavyRateModifier: 0.5 heavyDamageBaseModifier: 1.75 - heavyStaminaCost: 25 - maxTargets: 2 + heavyStaminaCost: 15 angle: 120 soundHit: collection: ExplosionSmall diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml index 68f8863d116..970a00ddf8f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml @@ -13,13 +13,12 @@ - type: MeleeWeapon wideAnimationRotation: -135 attackRate: 1.25 - range: 1.4 + range: 1.5 damage: types: Slash: 8 heavyRateModifier: 0.8 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 5 maxTargets: 3 angle: 40 soundHit: @@ -95,7 +94,6 @@ - type: MeleeWeapon wideAnimationRotation: -135 attackRate: 1.5 - range: 1.4 damage: types: Slash: 9 @@ -122,7 +120,6 @@ - type: MeleeWeapon wideAnimationRotation: -135 attackRate: 1.25 - range: 1.5 damage: types: Slash: 8 @@ -204,7 +201,7 @@ state: icon - type: MeleeWeapon attackRate: 1.75 - range: 0.75 + range: 1.4 damage: types: Slash: 5.5 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml index a1addba2625..38a203ce908 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml @@ -43,8 +43,8 @@ capacity: 1 count: 1 - type: MeleeWeapon - attackRate: 0.75 - range: 1.75 + attackRate: 0.85 + range: 1.65 wideAnimationRotation: -135 damage: types: @@ -53,7 +53,7 @@ bluntStaminaDamageFactor: 2.0 heavyRateModifier: 0.75 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 angle: 120 soundHit: collection: MetalThud @@ -92,7 +92,6 @@ Slash: 9 heavyRateModifier: 0.9 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 5 maxTargets: 2 angle: 20 - type: Tag diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml index 6ba659ccb40..fb1eb3d7fe9 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml @@ -11,8 +11,8 @@ sprite: Objects/Weapons/Melee/pickaxe.rsi state: pickaxe - type: MeleeWeapon - attackRate: 0.75 - range: 1.75 + attackRate: 0.85 + range: 1.5 wideAnimationRotation: -135 soundHit: path: "/Audio/Weapons/smash.ogg" @@ -24,9 +24,8 @@ Pierce: 3 bluntStaminaDamageFactor: 2.0 heavyDamageBaseModifier: 1.75 - heavyStaminaCost: 5 - maxTargets: 2 - angle: 60 + maxTargets: 5 + angle: 80 - type: Wieldable - type: IncreaseDamageOnWield damage: @@ -61,19 +60,17 @@ wideAnimationRotation: -90 soundHit: path: "/Audio/Items/drill_hit.ogg" - attackRate: 0.5 - range: 1.4 + attackRate: 1.2 + range: 1.5 damage: types: - Blunt: 9 + Blunt: 6 Slash: 3 Structural: 12 bluntStaminaDamageFactor: 4.0 heavyRateModifier: 1 heavyRangeModifier: 2 heavyDamageBaseModifier: 1 - heavyStaminaCost: 10 - maxTargets: 3 angle: 20 - type: ReverseEngineering # Nyano diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml index ecc84e50073..ffe791ce0c4 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml @@ -9,7 +9,7 @@ state: icon - type: MeleeWeapon wideAnimationRotation: -135 - attackRate: 0.8 + attackRate: 0.75 range: 1.75 damage: types: @@ -18,7 +18,7 @@ bluntStaminaDamageFactor: 2.0 heavyRateModifier: 0.75 heavyDamageBaseModifier: 1.75 - heavyStaminaCost: 15 + heavyStaminaCost: 7.5 maxTargets: 10 angle: 120 soundHit: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml index d8955b4defe..991a5553c79 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml @@ -30,17 +30,14 @@ energyPerUse: 70 - type: MeleeWeapon wideAnimationRotation: -135 - attackRate: 0.8 - range: 1.4 + attackRate: 1 + range: 1.5 damage: types: Blunt: 7.5 bluntStaminaDamageFactor: 2.0 heavyRateModifier: 0.8 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 5 - maxTargets: 3 - angle: 60 animation: WeaponArcThrust - type: StaminaDamageOnHit damage: 22 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 82b99ce37e3..5757f9c7168 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -57,9 +57,9 @@ types: Slash: 12 heavyRateModifier: 0.5 - heavyRangeModifier: 3 #Superior Japanese folded steel + heavyRangeModifier: 2.75 #Superior Japanese folded steel heavyDamageBaseModifier: 1.25 - heavyStaminaCost: 10 + heavyStaminaCost: 15 maxTargets: 1 angle: 20 - type: Item @@ -120,7 +120,7 @@ heavyRateModifier: 0.8 heavyRangeModifier: 1.25 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 angle: 80 soundHit: path: /Audio/Weapons/bladeslice.ogg @@ -151,7 +151,7 @@ heavyRateModifier: 0.5 heavyRangeModifier: 1 heavyDamageBaseModifier: 1 - heavyStaminaCost: 20 + heavyStaminaCost: 15 maxTargets: 10 angle: 200 soundHit: @@ -186,7 +186,7 @@ heavyRateModifier: 0.8 heavyRangeModifier: 1.2 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 maxTargets: 3 angle: 40 soundHit: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index a952713dd5f..2c72afa1d27 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -34,12 +34,10 @@ damage: types: Blunt: 7 - bluntStaminaDamageFactor: 2.0 + bluntStaminaDamageFactor: 2.5 heavyRateModifier: 0.75 heavyDamageBaseModifier: 1.75 - heavyStaminaCost: 5 - maxTargets: 3 - angle: 60 + heavyStaminaCost: 1 animation: WeaponArcSlash - type: StaminaDamageOnHit damage: 35 @@ -106,7 +104,7 @@ bluntStaminaDamageFactor: 2 heavyRateModifier: 1 heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 10 + heavyStaminaCost: 7.5 - type: Item size: Normal - type: Clothing diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml index 681f0a390c8..6e331a13a8c 100644 --- a/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml +++ b/Resources/Prototypes/Entities/Structures/Dispensers/chem.yml @@ -31,6 +31,9 @@ acts: ["Destruction"] - type: Machine board: ChemDispenserMachineCircuitboard + - type: UpgradePowerDraw + powerDrawMultiplier: 0.75 + scaling: Exponential - type: GuideHelp guides: - Chemicals diff --git a/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml b/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml index c1b0cf0423b..dbef5a7504a 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml @@ -20,6 +20,7 @@ - TableMask layer: - TableLayer + - BulletImpassable - type: SpriteFade - type: Sprite - type: Icon @@ -38,7 +39,8 @@ - type: FootstepModifier footstepSoundCollection: collection: FootstepHull - + - type: RequireProjectileTarget + - type: entity id: CounterBase parent: TableBase diff --git a/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml b/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml index e424b1b40b4..4c8427e46ee 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml @@ -137,7 +137,7 @@ acts: [ "Destruction" ] - trigger: !type:DamageTrigger - damage: 25 + damage: 15 behaviors: - !type:PlaySoundBehavior sound: @@ -178,7 +178,7 @@ acts: [ "Destruction" ] - trigger: !type:DamageTrigger - damage: 25 + damage: 15 behaviors: - !type:PlaySoundBehavior sound: @@ -216,7 +216,7 @@ acts: [ "Destruction" ] - trigger: !type:DamageTrigger - damage: 75 + damage: 30 behaviors: - !type:PlaySoundBehavior sound: @@ -263,7 +263,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 25 + damage: 15 behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] @@ -379,7 +379,7 @@ collection: GlassBreak - trigger: !type:DamageTrigger - damage: 50 + damage: 30 behaviors: - !type:PlaySoundBehavior sound: @@ -426,7 +426,7 @@ acts: [ "Destruction" ] - trigger: !type:DamageTrigger - damage: 25 + damage: 40 behaviors: - !type:PlaySoundBehavior sound: @@ -546,7 +546,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 50 + damage: 40 behaviors: - !type:PlaySoundBehavior sound: @@ -573,7 +573,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 50 + damage: 20 behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] diff --git a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml index 0fb69b4fdbd..14b3270ba88 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml @@ -33,7 +33,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 100 + damage: 25 behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] @@ -48,6 +48,7 @@ collection: MetalBreak - type: StaticPrice price: 10 + - type: RequireProjectileTarget #Starts unanchored, cannot be rotated while anchored - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml index b7ea7c6f6ca..9468ecd4523 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml @@ -62,11 +62,11 @@ priority: 1 - type: RequireProjectileTarget - type: LanguageSpeaker - currentLanguage: GalacticCommon + currentLanguage: TauCetiBasic - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - RobotTalk understands: - - GalacticCommon + - TauCetiBasic - RobotTalk diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 4fd2129f7a5..39fd34016b9 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -1,8 +1,8 @@ - type: entity parent: BaseComputer id: ComputerAlert - name: alerts computer - description: Used to access the station's automated alert system. + name: atmospheric alerts computer + description: Used to access the station's automated atmospheric alert system. components: - type: Computer board: AlertsComputerCircuitboard @@ -13,9 +13,33 @@ - map: ["computerLayerKeyboard"] state: generic_keyboard - map: ["computerLayerScreen"] - state: alert-2 + state: alert-0 - map: ["computerLayerKeys"] state: atmos_key + - type: GenericVisualizer + visuals: + enum.ComputerVisuals.Powered: + computerLayerScreen: + True: { visible: true, shader: unshaded } + False: { visible: false } + computerLayerKeys: + True: { visible: true, shader: unshaded } + False: { visible: true, shader: shaded } + enum.AtmosAlertsComputerVisuals.ComputerLayerScreen: + computerLayerScreen: + 0: { state: alert-0 } + 1: { state: alert-0 } + 2: { state: alert-1 } + 3: { state: alert-2 } + 4: { state: alert-2 } + - type: AtmosAlertsComputer + - type: ActivatableUI + singleUser: true + key: enum.AtmosAlertsComputerUiKey.Key + - type: UserInterface + interfaces: + - key: enum.AtmosAlertsComputerUiKey.Key + type: AtmosAlertsComputerBoundUserInterface - type: entity parent: BaseComputer diff --git a/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml b/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml index ab09a03fef7..e6f08fe8467 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/anomaly_equipment.yml @@ -176,8 +176,8 @@ SetParticleZeta: AnomalousParticleZeta SetParticleSigma: AnomalousParticleSigma fireBurstSize: 1 - fireBurstDelayMin: 2 - fireBurstDelayMax: 6 + baseFireBurstDelayMin: 2 + baseFireBurstDelayMax: 6 - type: ApcPowerReceiver powerLoad: 100 - type: GuideHelp diff --git a/Resources/Prototypes/Entities/Structures/Machines/anomaly_sync.yml b/Resources/Prototypes/Entities/Structures/Machines/anomaly_sync.yml index 3019b8adf83..eeaae611c37 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/anomaly_sync.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/anomaly_sync.yml @@ -41,7 +41,7 @@ bounds: "-0.35,-0.35,0.35,0.35" density: 100 mask: - - ItemMask + - ItemMask hard: True - type: Transform anchored: true @@ -49,6 +49,9 @@ - type: ApcPowerReceiver powerLoad: 2500 needsPower: true + - type: UpgradePowerDraw + powerDrawMultiplier: 0.80 + scaling: Exponential - type: ItemPlacer whitelist: components: diff --git a/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml b/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml index 18999a6ab2a..2bea530e908 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml @@ -107,6 +107,9 @@ hard: False - type: Transform noRot: false + - type: UpgradePowerDraw + powerDrawMultiplier: 0.80 + scaling: Exponential - type: TraversalDistorter - type: ItemPlacer # don't limit the number of artifacts that can be biased diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index e5a9cb35417..52e9096791b 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -294,6 +294,12 @@ - Implanter - PillCanister - ChemistryEmptyBottle01 + - AdvancedCapacitorStockPart + - AdvancedMatterBinStockPart + - NanoManipulatorStockPart + - SuperCapacitorStockPart + - SuperMatterBinStockPart + - PicoManipulatorStockPart - AdvMopItem - WeaponSprayNozzle - ClothingBackpackWaterTank diff --git a/Resources/Prototypes/Entities/Structures/Machines/seed_extractor.yml b/Resources/Prototypes/Entities/Structures/Machines/seed_extractor.yml index ec07294bfb1..4ecd1c29e27 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/seed_extractor.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/seed_extractor.yml @@ -35,3 +35,6 @@ - type: SeedExtractor - type: Machine board: SeedExtractorMachineCircuitboard + - type: UpgradePowerDraw + powerDrawMultiplier: 0.75 + scaling: Exponential diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index 02cdd80af35..f3f68660b2d 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -105,10 +105,10 @@ - type: WiresVisuals - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - RobotTalk understands: - - GalacticCommon + - TauCetiBasic - RobotTalk - type: VendingMachine soundVend: /Audio/SimpleStation14/Machines/machine_vend.ogg diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml index 4fa1bcbd918..35d65ffe87a 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml @@ -171,6 +171,9 @@ materialWhiteList: [Plasma] - type: PortableGenerator startChance: 0.8 + - type: UpgradePowerSupplier + powerSupplyMultiplier: 1.25 + scaling: Exponential - type: GeneratorExhaustGas gasType: CarbonDioxide # 2 moles of gas for every sheet of plasma. @@ -228,6 +231,9 @@ storageLimit: 3000 materialWhiteList: [Uranium] - type: PortableGenerator + - type: UpgradePowerSupplier + powerSupplyMultiplier: 1.25 + scaling: Exponential - type: PowerMonitoringDevice group: Generator loadNodes: diff --git a/Resources/Prototypes/Entities/Structures/Power/smes.yml b/Resources/Prototypes/Entities/Structures/Power/smes.yml index 1e3559e5a95..762e8d375f6 100644 --- a/Resources/Prototypes/Entities/Structures/Power/smes.yml +++ b/Resources/Prototypes/Entities/Structures/Power/smes.yml @@ -29,10 +29,15 @@ state: "smes-op1" shader: unshaded - type: Smes + - type: UpgradeBattery + maxChargeMultiplier: 2 + baseMaxCharge: 8000000 + - type: UpgradePowerSupplyRamping + scaling: Linear + supplyRampingMultiplier: 1 - type: Appearance - type: Battery startingCharge: 0 - maxCharge: 8000000 - type: ExaminableBattery - type: NodeContainer examinable: true diff --git a/Resources/Prototypes/Entities/Structures/Power/substation.yml b/Resources/Prototypes/Entities/Structures/Power/substation.yml index 347b18ecaee..489cfff6597 100644 --- a/Resources/Prototypes/Entities/Structures/Power/substation.yml +++ b/Resources/Prototypes/Entities/Structures/Power/substation.yml @@ -17,8 +17,13 @@ shader: unshaded - state: full shader: unshaded + - type: UpgradeBattery + maxChargeMultiplier: 2 + baseMaxCharge: 2500000 + - type: UpgradePowerSupplyRamping + scaling: Linear + supplyRampingMultiplier: 1 - type: Battery - maxCharge: 2500000 startingCharge: 0 - type: ExaminableBattery - type: PointLight diff --git a/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml b/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml index f87be426598..eb299e3f3a6 100644 --- a/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml +++ b/Resources/Prototypes/Entities/Structures/Shuttles/thrusters.yml @@ -143,6 +143,7 @@ - type: Thruster thrusterType: Angular requireSpace: false + baseThrust: 2000 thrust: 2000 machinePartThrust: Manipulator - type: Sprite @@ -192,6 +193,9 @@ collection: MetalBreak - !type:ChangeConstructionNodeBehavior node: machineFrame + - type: UpgradePowerDraw + powerDrawMultiplier: 0.75 + scaling: Exponential - type: Damageable damageContainer: Inorganic damageModifierSet: Electronic @@ -216,6 +220,7 @@ - type: Thruster thrusterType: Angular requireSpace: false + baseThrust: 100 thrust: 100 - type: ApcPowerReceiver needsPower: false diff --git a/Resources/Prototypes/Entities/Structures/Specific/oracle.yml b/Resources/Prototypes/Entities/Structures/Specific/oracle.yml index b60906b55e0..1e3b9a5b7e8 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/oracle.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/oracle.yml @@ -67,6 +67,13 @@ demandWhitelist: components: - Item + - type: LanguageSpeaker + currentLanguage: TauCetiBasic + - type: LanguageKnowledge + speaks: + - TauCetiBasic + understands: + - TauCetiBasic - type: weightedRandomEntity diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml index 62f2000593b..70e204f694e 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml @@ -53,6 +53,8 @@ - AirAlarm - type: AtmosDevice - type: AirAlarm + - type: AtmosAlertsDevice + group: AirAlarm - type: Clickable - type: InteractionOutline - type: UserInterface diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml index 05988fbc217..c39adcde3b6 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/fire_alarm.yml @@ -42,6 +42,8 @@ - type: Clickable - type: InteractionOutline - type: FireAlarm + - type: AtmosAlertsDevice + group: FireAlarm - type: ContainerFill containers: board: [ FireAlarmElectronics ] diff --git a/Resources/Prototypes/Entities/Structures/plastic_flaps.yml b/Resources/Prototypes/Entities/Structures/plastic_flaps.yml index c4ee507395f..207305d2327 100644 --- a/Resources/Prototypes/Entities/Structures/plastic_flaps.yml +++ b/Resources/Prototypes/Entities/Structures/plastic_flaps.yml @@ -26,6 +26,7 @@ - TabletopMachineMask layer: - MidImpassable + - BulletImpassable - type: Damageable damageContainer: StructuralInorganic damageModifierSet: Metallic @@ -33,7 +34,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 100 + damage: 50 behaviors: - !type:DoActsBehavior acts: ["Destruction"] @@ -45,7 +46,8 @@ node: plasticFlaps - type: StaticPrice price: 83 - + - type: RequireProjectileTarget + - type: entity id: PlasticFlapsOpaque parent: PlasticFlapsClear @@ -65,6 +67,7 @@ layer: - Opaque - MidImpassable + - BulletImpassable - type: Occluder - type: Construction graph: PlasticFlapsGraph @@ -81,7 +84,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 150 + damage: 75 behaviors: - !type:DoActsBehavior acts: ["Destruction"] @@ -103,7 +106,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 150 + damage: 75 behaviors: - !type:DoActsBehavior acts: ["Destruction"] diff --git a/Resources/Prototypes/Guidebook/science.yml b/Resources/Prototypes/Guidebook/science.yml index a21be1678ce..c73b666f687 100644 --- a/Resources/Prototypes/Guidebook/science.yml +++ b/Resources/Prototypes/Guidebook/science.yml @@ -7,6 +7,7 @@ - AnomalousResearch - Xenoarchaeology - Robotics + - MachineUpgrading - Psionics # Nyanotrasen - Psionics guidebook # - AltarsGolemancy # When it's added # Nyanotrasen - Golemancy guidebook - ReverseEngineering # Nyanotrasen - Reverse Engineering guidebook @@ -60,6 +61,11 @@ name: guide-entry-traversal-distorter text: "/ServerInfo/Guidebook/Science/TraversalDistorter.xml" +- type: guideEntry + id: MachineUpgrading + name: guide-entry-machine-upgrading + text: "/ServerInfo/Guidebook/Science/MachineUpgrading.xml" + - type: guideEntry id: Cyborgs name: guide-entry-cyborgs diff --git a/Resources/Prototypes/Language/Species-Specific/diona.yml b/Resources/Prototypes/Language/Species-Specific/diona.yml new file mode 100644 index 00000000000..76b62163ff3 --- /dev/null +++ b/Resources/Prototypes/Language/Species-Specific/diona.yml @@ -0,0 +1,17 @@ +# Spoken by dionas. +# TODO: Replace this with a much better language. +- type: language + id: RootSpeak + speech: + color: "#ce5e14dd" + fontId: Noganas + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 5 + replacement: + - hs + - zt + - kr + - st + - sh \ No newline at end of file diff --git a/Resources/Prototypes/Language/Species-Specific/moth.yml b/Resources/Prototypes/Language/Species-Specific/moth.yml new file mode 100644 index 00000000000..ed0a6009c59 --- /dev/null +++ b/Resources/Prototypes/Language/Species-Specific/moth.yml @@ -0,0 +1,69 @@ +# Spoken by moths. +# TODO: Replace this with a much better language. +- type: language + id: Moffic + speech: + color: "#c7df2edd" + fontId: Copperplate + obfuscation: + !type:SyllableObfuscation + minSyllables: 2 # Replacements are really short + maxSyllables: 4 + replacement: + - år + - i + - går + - sek + - mo + - ff + - ok + - gj + - ø + - gå + - la + - le + - lit + - ygg + - van + - dår + - næ + - møt + - idd + - hvo + - ja + - på + - han + - så + - ån + - det + - att + - nå + - gö + - bra + - int + - tyc + - om + - när + - två + - må + - dag + - sjä + - vii + - vuo + - eil + - tun + - käyt + - teh + - vä + - hei + - huo + - suo + - ää + - ten + - ja + - heu + - stu + - uhr + - kön + - we + - hön \ No newline at end of file diff --git a/Resources/Prototypes/Language/Species-Specific/nekomimetic.yml b/Resources/Prototypes/Language/Species-Specific/nekomimetic.yml new file mode 100644 index 00000000000..3610e5fa50e --- /dev/null +++ b/Resources/Prototypes/Language/Species-Specific/nekomimetic.yml @@ -0,0 +1,60 @@ +# A mess of broken Japanese, spoken by Felinds and Oni +# TODO: Replace this with a much better language. +- type: language + id: Nekomimetic + speech: + color: "#df57aaee" + fontId: Manga + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 # May be too long even, we'll see. + replacement: + - neko + - nyan + - mimi + - moe + - mofu + - fuwa + - kyaa + - kawaii + - poka + - munya + - puni + - munyu + - ufufu + - icha + - doki + - kyun + - kusu + - nya + - nyaa + - desu + - kis + - ama + - chuu + - baka + - hewo + - boop + - gato + - kit + - sune + - yori + - sou + - baka + - chan + - san + - kun + - mahou + - yatta + - suki + - usagi + - domo + - ori + - uwa + - zaazaa + - shiku + - puru + - ira + - heto + - etto \ No newline at end of file diff --git a/Resources/Prototypes/Language/Species-Specific/reptilian.yml b/Resources/Prototypes/Language/Species-Specific/reptilian.yml new file mode 100644 index 00000000000..b22a62ee0e3 --- /dev/null +++ b/Resources/Prototypes/Language/Species-Specific/reptilian.yml @@ -0,0 +1,95 @@ +# Spoken by the Lizard race. +# TODO: Replace this with a much better language. +- type: language + id: Draconic + speech: + color: "#2aca2add" + obfuscation: + !type:SyllableObfuscation + minSyllables: 2 + maxSyllables: 4 + replacement: + - za + - az + - ze + - ez + - zi + - iz + - zo + - oz + - zu + - uz + - zs + - sz + - ha + - ah + - he + - eh + - hi + - ih + - ho + - oh + - hu + - uh + - hs + - sh + - la + - al + - le + - el + - li + - il + - lo + - ol + - lu + - ul + - ls + - sl + - ka + - ak + - ke + - ek + - ki + - ik + - ko + - ok + - ku + - uk + - ks + - sk + - sa + - as + - se + - es + - si + - is + - so + - os + - su + - us + - ss + - ss + - ra + - ar + - re + - er + - ri + - ir + - ro + - or + - ru + - ur + - rs + - sr + - a + - a + - e + - e + - i + - i + - o + - o + - u + - u + - s + - s \ No newline at end of file diff --git a/Resources/Prototypes/Language/Species-Specific/slimeperson.yml b/Resources/Prototypes/Language/Species-Specific/slimeperson.yml new file mode 100644 index 00000000000..a3c1f91a170 --- /dev/null +++ b/Resources/Prototypes/Language/Species-Specific/slimeperson.yml @@ -0,0 +1,17 @@ +# Spoken by slimes. +# TODO: Replace this with a much better language. +- type: language + id: Bubblish + speech: + color: "#00a3e2dd" + fontId: RubikBubbles + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - blob + - plop + - pop + - bop + - boop \ No newline at end of file diff --git a/Resources/Prototypes/Language/Species-Specific/vulpkanin.yml b/Resources/Prototypes/Language/Species-Specific/vulpkanin.yml new file mode 100644 index 00000000000..17a48493a49 --- /dev/null +++ b/Resources/Prototypes/Language/Species-Specific/vulpkanin.yml @@ -0,0 +1,68 @@ +# Spoken by the Vulpkanin race. +# TODO: Replace this with a much better language. +- type: language + id: Canilunzt + speech: + color: "#d69b3dcc" + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 4 + replacement: + - rur + - ya + - cen + - rawr + - bar + - kuk + - tek + - qat + - uk + - wu + - vuh + - tah + - tch + - schz + - auch + - ist + - ein + - entch + - zwichs + - tut + - mir + - wo + - bis + - es + - vor + - nic + - gro + - enem + - zandt + - tzch + - noch + - hel + - ischt + - far + - wa + - baram + - iereng + - tech + - lach + - sam + - mak + - lich + - gen + - or + - ag + - eck + - gec + - stag + - onn + - bin + - ket + - jarl + - vulf + - einech + - cresthz + - azunein + - ghzth \ No newline at end of file diff --git a/Resources/Prototypes/Language/Standard/elyran.yml b/Resources/Prototypes/Language/Standard/elyran.yml new file mode 100644 index 00000000000..c2c90e7b6f0 --- /dev/null +++ b/Resources/Prototypes/Language/Standard/elyran.yml @@ -0,0 +1,86 @@ +- type: language + id: Elyran + speech: + color: "#8282fbaa" + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 4 + replacement: + - af + - if + - ba + - ta + - tha + - id + - jem + - ha + - kha + - dal + - dhl + - ra + - zay + - sen + - um + - shn + - sid + - ad + - ta + - za + - ayn + - gha + - zir + - yn + - fa + - qaf + - iam + - mim + - al + - ja + - non + - ha + - waw + - ya + - hem + - zah + - hml + - ks + - ini + - da + - ks + - iga + - ih + - la + - ulf + - xe + - ayw + - sit + - ah + - aarah + - jalaa + - sirt + - kurt + - turhk + - ust + - irk + - kir + - mir + - ach + - oglu + - bolu + - shek + - she + - ghoz + - miya + - ejdan + - haaz + - quq + - taab + - shanha + - an + - saa + - seh + - an' + - e' + - a' + - em' \ No newline at end of file diff --git a/Resources/Prototypes/Language/Standard/freespeak.yml b/Resources/Prototypes/Language/Standard/freespeak.yml new file mode 100644 index 00000000000..831c2a67ff8 --- /dev/null +++ b/Resources/Prototypes/Language/Standard/freespeak.yml @@ -0,0 +1,259 @@ +- type: language + id: Freespeak + speech: + color: "#597d35" + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - a + - aan + - aas + - ab + - aba + - ad + - aee + - aft + - ag + - ai + - aise + - ak + - akee + - aq + - ar + - ata + - aur + - aus + - ba + - baat + - bach + - bad + - bahe + - band + - be + - ben + - ber + - bhaa + - bhu + - bra + - burt + - cap + - cer + - ch + - cha + - chaar + - chale + - chalo + - chil + - com + - da + - daa + - daaj + - dat + - de + - dee + - dhaa + - di + - die + - dik + - din + - diz + - do + - dos + - dosh + - durch + - eer + - ek + - er + - es + - fal + - fang + - fra + - fun + - ga + - gan + - gao + - gee + - geet + - gern + - gir + - gon + - gren + - gri + - gu + - guda + - ha + - haa + - hai + - hain + - har + - hat + - he + - hee + - heer + - hekt + - heu + - hit + - hn + - ho + - hua + - huk + - hul + - ich + - ig + - in + - isch + - ja + - jaa + - jad + - jan + - jao + - jar + - jas + - jee + - jiao + - jin + - jing + - un + - ka + - kaha + - kana + - kar + - kara + - karo + - ke + - kee + - keln + - kha + - khada + - khe + - khi + - ko + - koo + - ky + - la + - laa + - laat + - lad + - lada + - lana + - lane + - le + - lee + - leiden + - leis + - len + - lie + - lo + - maa + - maan + - mod + - most + - muj + - mujhe + - mukt + - na + - naya + - ne + - nee + - net + - neta + - nir + - nka + - oon + - oop + - pa + - paa + - pet + - phen + - phot + - pi + - plo + - pra + - que + - ra + - raa + - rahe + - raho + - ran + - rana + - rar + - re + - ri + - rie + - rin + - ro + - rona + - rosh + - rtiv + - saa + - saal + - saath + - san + - santu + - sch + - se + - sen + - sh + - sha + - shee + - shi + - shn + - sht + - shuo + - soch + - sol + - soo + - ssa + - ster + - suk + - sur + - ta + - taan + - tak + - taka + - tal + - tan + - tar + - ten + - tend + - th + - tho + - tili + - to + - ton + - tr + - tu + - tum + - tung + - udaa + - ugr + - unge + - ut + - va + - vaa + - vaad + - vaib + - ve + - ven + - ver + - vi + - vis + - vol + - wic + - wu + - wut + - xi + - xiao + - ya + - yah + - yon + - you + - zas + - ze + - zhu + - zi + - zo + - zorn + - zt diff --git a/Resources/Prototypes/Language/Standard/solcommon.yml b/Resources/Prototypes/Language/Standard/solcommon.yml new file mode 100644 index 00000000000..88c5470154b --- /dev/null +++ b/Resources/Prototypes/Language/Standard/solcommon.yml @@ -0,0 +1,258 @@ +- type: language + id: SolCommon + speech: + color: "#8282fbaa" + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 4 + replacement: + - a + - abe + - ade + - ai + - an + - ana + - ba + - bae + - bai + - bang + - bao + - bei + - ben + - beo + - bi + - bian + - bing + - bo + - bu + - bugu + - bun + - cai + - can + - cao + - cau + - chan + - chen + - cheong + - chiu + - chong + - chyo + - da + - dan + - dao + - de + - deun + - duo + - eon + - eun + - eusi + - feng + - fu + - ga + - gak + - gan + - gang + - gao + - ge + - gei + - gen + - geo + - gil + - go + - gou + - gu + - gua + - gui + - gul + - gun + - guo + - gwi + - ha + - hai + - hal + - han + - hap + - hara + - he + - hego + - hen + - hon + - hoo + - hu + - hua + - hun + - hyeong + - i + - jae + - jeo + - jeon + - ji + - jia + - jian + - jiang + - jie + - jong + - ju + - jue + - juede + - jung + - juzi + - ka + - kang + - kawa + - ke + - keun + - ki + - kin + - ko + - kore + - kou + - ku + - kuda + - kun + - kyu + - lang + - lao + - leng + - leung + - li + - lian + - liang + - lie + - ling + - lizi + - lleo + - long + - lu + - ma + - mah + - me + - mei + - meinu + - men + - meng + - meog + - meoni + - mi + - mian + - min + - mo + - mot + - mu + - mun + - na + - nae + - nai + - nari + - ne + - ni + - nii + - nim + - nin + - nop + - nu + - o + - oba + - oga + - oji + - oka + - ong + - op + - oto + - pa + - pai + - pang + - pin + - ping + - pong + - pu + - pum + - pye + - qi + - qie + - qing + - ra + - rei + - ren + - ri + - ru + - ruan + - sa + - sai + - sama + - san + - sang + - se + - sei + - sen + - seo + - seon + - seong + - shang + - shen + - sheng + - shi + - sho + - shui + - si + - su + - sui + - sum + - sun + - swi + - ta + - tae + - tai + - tame + - tamen + - tan + - te + - tei + - ti + - tian + - to + - ton + - tsu + - ul + - wa + - wan + - wang + - wei + - wo + - xi + - xian + - xiao + - xing + - xiong + - xiu + - xu + - xuan + - xue + - ya + - yan + - yang + - yeong + - yi + - yige + - yin + - ying + - yiqi + - yong + - you + - yu + - yuli + - yuyi + - zai + - zao + - zhan + - zhang + - zhe + - zhen + - zheng + - zhuo + - zi + - zo + - zu + - zun + - zuo \ No newline at end of file diff --git a/Resources/Prototypes/Language/Standard/taucetibasic.yml b/Resources/Prototypes/Language/Standard/taucetibasic.yml new file mode 100644 index 00000000000..96426f6c24f --- /dev/null +++ b/Resources/Prototypes/Language/Standard/taucetibasic.yml @@ -0,0 +1,256 @@ +# The "Default Language" other than Universal. +- type: language + id: TauCetiBasic + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - a + - ado + - ago + - aj + - ajn + - al + - alt + - am + - amas + - an + - ang + - ante + - ap + - ard + - arma + - aro + - as + - aur + - aut + - aw + - ba + - bal + - bao + - be + - beau + - bel + - bi + - bit + - blu + - bo + - bod + - boj + - bojn + - bu + - but + - ca + - caj + - ce + - cer + - chun + - ci + - cion + - coj + - cor + - da + - daj + - dan + - de + - den + - dis + - do + - dor + - dorm + - eco + - ego + - ek + - eks + - en + - ero + - es + - est + - et + - eve + - fa + - fe + - fel + - fla + - foj + - fra + - fraz + - fros + - ful + - fut + - ga + - gan + - gar + - gi + - gis + - go + - gran + - ha + - han + - hav + - hom + - hong + - hu + - hum + - hushi + - ia + - iaj + - ica + - id + - idon + - il + - in + - ing + - io + - is + - iton + - iza + - ja + - ji + - jirou + - joj + - ka + - kaj + - kajo + - kan + - ke + - ket + - ki + - kna + - krio + - ku + - kui + - kuk + - kun + - kur + - la + - laca + - leng + - les + - li + - liao + - lib + - ling + - lis + - lo + - lon + - long + - lu + - lud + - ma + - mal + - man + - me + - mego + - mero + - mi + - mia + - min + - mo + - moj + - mola + - mon + - mul + - ne + - ner + - ni + - nio + - nu + - of + - oj + - om + - ou + - pe + - pi + - plan + - pli + - po + - por + - post + - pre + - prin + - pru + - pu + - pur + - qiu + - que + - ra + - ras + - re + - ri + - rig + - ril + - ro + - roj + - ron + - roso + - rou + - ru + - sa + - san + - sci + - sek + - shi + - shiia + - shiue + - shiwu + - shu + - shui + - si + - siaj + - sku + - so + - som + - sti + - str + - stre + - su + - suno + - ta + - tan + - tas + - te + - tel + - tem + - the + - ti + - tian + - tita + - tiu + - to + - toj + - ton + - tran + - tre + - tri + - trin + - tro + - trus + - un + - undo + - uno + - uz + - va + - var + - varm + - vas + - ve + - vek + - ven + - ves + - vi + - via + - vin + - vino + - vint + - vir + - von + - vu + - whe + - wu + - yong + - zem + - zo + - zoj + - zon \ No newline at end of file diff --git a/Resources/Prototypes/Language/Standard/tradeband.yml b/Resources/Prototypes/Language/Standard/tradeband.yml new file mode 100644 index 00000000000..96415dc5a6e --- /dev/null +++ b/Resources/Prototypes/Language/Standard/tradeband.yml @@ -0,0 +1,258 @@ +- type: language + id: Tradeband + speech: + color: "#597d35" + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - a + - acc + - ai + - al + - ali + - am + - ama + - ami + - amo + - an + - ang + - arme + - ave + - ba + - bai + - bar + - bat + - bi + - blie + - bris + - ca + - can + - cant + - car + - care + - ce + - ci + - cis + - cit + - cla + - co + - cul + - cur + - curt + - da + - dam + - dans + - de + - di + - dier + - dim + - dins + - dorm + - du + - duro + - e + - eaux + - ec + - ecto + - ees + - ego + - el + - en + - ent + - er + - ere + - eres + - eri + - ero + - es + - et + - ex + - far + - fi + - fic + - fine + - fol + - foll + - fri + - fro + - gen + - gil + - go + - gran + - hab + - ho + - huc + - ia + - iam + - ibus + - idor + - ie + - iens + - ier + - ieur + - iis + - il + - in + - ine + - int + - ir + - is + - ise + - it + - itt + - jar + - je + - jo + - jor + - la + - lar + - lav + - le + - lees + - ler + - les + - li + - lib + - lie + - lo + - lu + - ma + - man + - manu + - mar + - mari + - mas + - me + - mea + - mee + - mejo + - men + - mes + - meum + - meus + - mi + - mier + - min + - mine + - mit + - mo + - moi + - mon + - mons + - mors + - mou + - mul + - na + - nam + - ne + - nee + - nent + - nes + - ni + - nit + - nom + - nu + - num + - o + - oc + - occ + - oja + - om + - omni + - or + - ori + - oro + - os + - ou + - oub + - pa + - par + - pars + - pas + - plu + - pluv + - po + - pol + - pos + - pou + - pous + - pre + - pu + - pug + - pus + - que + - qui + - re + - ri + - ric + - riga + - rito + - ro + - rom + - sa + - sal + - se + - ser + - sers + - ses + - sim + - sion + - so + - sol + - som + - sou + - sper + - sse + - ste + - su + - suis + - sul + - sur + - ta + - tar + - te + - teau + - tem + - temp + - ten + - tene + - tes + - ti + - tibus + - tien + - tion + - to + - tol + - ton + - tons + - tout + - tra + - trai + - tre + - trou + - tuo + - tus + - tut + - ues + - ui + - ul + - um + - un + - upa + - us + - ut + - ux + - va + - vail + - ve + - ven + - veni + - vi + - viam + - vie + - vo + - xus + - za + - zio diff --git a/Resources/Prototypes/Language/animal.yml b/Resources/Prototypes/Language/animal.yml new file mode 100644 index 00000000000..46178200011 --- /dev/null +++ b/Resources/Prototypes/Language/animal.yml @@ -0,0 +1,186 @@ +# Languages spoken by various critters. +- type: language + id: Cat + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 2 + replacement: + - murr + - meow + - purr + - mrow + +- type: language + id: Dog + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 2 + replacement: + - woof + - bark + - ruff + - bork + - raff + - garr + +- type: language + id: Fox + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 2 + replacement: + - ruff + - raff + - garr + - yip + - yap + - myah + +- type: language + id: Xeno + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 8 # I was crazy once + replacement: + - s + - S + +- type: language + id: Monkey + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 8 # They locked me in a room... + replacement: + - o + - k + +- type: language + id: Mouse + obfuscation: + !type:SyllableObfuscation + minSyllables: 2 + maxSyllables: 3 + replacement: + - squ + - eak + - pi + - ep + - chuu + - ee + - fwi + - he + +- type: language + id: Chicken + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - co + - coo + - ot + +- type: language + id: Duck + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - qu + - ack + - quack + +- type: language + id: Cow + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - moo + - mooo + +- type: language + id: Sheep + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - ba + - baa + - aa + +- type: language + id: Kangaroo + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - shre + - ack + - chuu + - choo + +- type: language + id: Pig + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - oink # Please someone come up with something better + +- type: language + id: Crab + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 3 + replacement: + - click + - clack + - ti + - pi + - tap + - cli + - ick + +- type: language + id: Kobold + obfuscation: + !type:SyllableObfuscation + minSyllables: 2 + maxSyllables: 4 + replacement: + - yip + - yap + - gar + - grr + - ar + - scre + - et + - gronk + - hiss + - ss + - ee + +- type: language + id: Hissing + obfuscation: + !type:SyllableObfuscation + minSyllables: 2 + maxSyllables: 4 + replacement: + - hss + - iss + - ss + - is diff --git a/Resources/Prototypes/Language/genericlanguages.yml b/Resources/Prototypes/Language/genericlanguages.yml new file mode 100644 index 00000000000..8d172cdae9c --- /dev/null +++ b/Resources/Prototypes/Language/genericlanguages.yml @@ -0,0 +1,45 @@ +# The universal language. This is technically used as a fallback for simulating the pre-languages +# style of Chat, and is not normally accessible by players. It is however used by characters +# with the Xenoglossy psionic power, as well as Ghosts, and AGhosts. +- type: language + id: Universal + obfuscation: + !type:ReplacementObfuscation + replacement: + - "*incomprehensible*" # Never actually used + +# Used by Robots. +# TODO: Replace this with much better languages. Yes, robots can have languages. +- type: language + id: RobotTalk + speech: + fontId: Monospace + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 + maxSyllables: 10 # Crazy + replacement: + - 0 + - 1 + +# Example of a sign language. Used by the Sign Language trait. +- type: language + id: Sign + speech: + allowRadio: false + requireSpeech: false + color: "#dddddd" + messageWrapOverrides: + Speak: chat-sign-language-message-wrap + Whisper: chat-sign-language-whisper-wrap + speechVerbOverrides: + - chat-speech-verb-sign-1 + - chat-speech-verb-sign-2 + - chat-speech-verb-sign-3 + obfuscation: + !type:ReplacementObfuscation + replacement: + - something + - a cryptic message + - a signal + - a message diff --git a/Resources/Prototypes/Language/languages.yml b/Resources/Prototypes/Language/languages.yml deleted file mode 100644 index 65af93e02d4..00000000000 --- a/Resources/Prototypes/Language/languages.yml +++ /dev/null @@ -1,614 +0,0 @@ -# The universal language, assumed if the entity has a UniversalLanguageSpeakerComponent. -# Do not use otherwise. Making an entity explicitly understand/speak this language will NOT have the desired effect. -- type: language - id: Universal - obfuscation: - !type:ReplacementObfuscation - replacement: - - "*incomprehensible*" # Never actually used - -# The common galactic tongue. -- type: language - id: GalacticCommon - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 3 - replacement: - - blah - - blah - - blah - - dingle-doingle - - dingle - - dangle - - jibber-jabber - - jubber - - bleh - - zippity - - zoop - - wibble - - wobble - - wiggle - - yada - - meh - - neh - - nah - - wah - -# Spoken by slimes. -- type: language - id: Bubblish - speech: - color: "#00a3e2dd" - fontId: RubikBubbles - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 3 - replacement: - - blob - - plop - - pop - - bop - - boop - -# Spoken by moths. -- type: language - id: Moffic - speech: - color: "#c7df2edd" - fontId: Copperplate - obfuscation: - !type:SyllableObfuscation - minSyllables: 2 # Replacements are really short - maxSyllables: 4 - replacement: - - år - - i - - går - - sek - - mo - - ff - - ok - - gj - - ø - - gå - - la - - le - - lit - - ygg - - van - - dår - - næ - - møt - - idd - - hvo - - ja - - på - - han - - så - - ån - - det - - att - - nå - - gö - - bra - - int - - tyc - - om - - när - - två - - må - - dag - - sjä - - vii - - vuo - - eil - - tun - - käyt - - teh - - vä - - hei - - huo - - suo - - ää - - ten - - ja - - heu - - stu - - uhr - - kön - - we - - hön - -# Spoken by dionas. -- type: language - id: RootSpeak - speech: - color: "#ce5e14dd" - fontId: Noganas - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 5 - replacement: - - hs - - zt - - kr - - st - - sh - -# A mess of broken Japanese, spoken by Felinds and Oni -- type: language - id: Nekomimetic - speech: - color: "#df57aaee" - fontId: Manga - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 3 # May be too long even, we'll see. - replacement: - - neko - - nyan - - mimi - - moe - - mofu - - fuwa - - kyaa - - kawaii - - poka - - munya - - puni - - munyu - - ufufu - - icha - - doki - - kyun - - kusu - - nya - - nyaa - - desu - - kis - - ama - - chuu - - baka - - hewo - - boop - - gato - - kit - - sune - - yori - - sou - - baka - - chan - - san - - kun - - mahou - - yatta - - suki - - usagi - - domo - - ori - - uwa - - zaazaa - - shiku - - puru - - ira - - heto - - etto - -# Spoken by the Lizard race. -- type: language - id: Draconic - speech: - color: "#2aca2add" - obfuscation: - !type:SyllableObfuscation - minSyllables: 2 - maxSyllables: 4 - replacement: - - za - - az - - ze - - ez - - zi - - iz - - zo - - oz - - zu - - uz - - zs - - sz - - ha - - ah - - he - - eh - - hi - - ih - - ho - - oh - - hu - - uh - - hs - - sh - - la - - al - - le - - el - - li - - il - - lo - - ol - - lu - - ul - - ls - - sl - - ka - - ak - - ke - - ek - - ki - - ik - - ko - - ok - - ku - - uk - - ks - - sk - - sa - - as - - se - - es - - si - - is - - so - - os - - su - - us - - ss - - ss - - ra - - ar - - re - - er - - ri - - ir - - ro - - or - - ru - - ur - - rs - - sr - - a - - a - - e - - e - - i - - i - - o - - o - - u - - u - - s - - s - -# Spoken by the Vulpkanin race. -- type: language - id: Canilunzt - speech: - color: "#d69b3dcc" - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 4 - replacement: - - rur - - ya - - cen - - rawr - - bar - - kuk - - tek - - qat - - uk - - wu - - vuh - - tah - - tch - - schz - - auch - - ist - - ein - - entch - - zwichs - - tut - - mir - - wo - - bis - - es - - vor - - nic - - gro - - enem - - zandt - - tzch - - noch - - hel - - ischt - - far - - wa - - baram - - iereng - - tech - - lach - - sam - - mak - - lich - - gen - - or - - ag - - eck - - gec - - stag - - onn - - bin - - ket - - jarl - - vulf - - einech - - cresthz - - azunein - - ghzth - -# The common language of the Sol system. -- type: language - id: SolCommon - speech: - color: "#8282fbaa" - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 4 - replacement: - - tao - - shi - - tzu - - yi - - com - - be - - is - - i - - op - - vi - - ed - - lec - - mo - - cle - - te - - dis - - e - -- type: language - id: RobotTalk - speech: - fontId: Monospace - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 10 # Crazy - replacement: - - 0 - - 1 - -# Languages spoken by various critters. -- type: language - id: Cat - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 2 - replacement: - - murr - - meow - - purr - - mrow - -- type: language - id: Dog - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 2 - replacement: - - woof - - bark - - ruff - - bork - - raff - - garr - -- type: language - id: Fox - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 2 - replacement: - - ruff - - raff - - garr - - yip - - yap - - myah - -- type: language - id: Xeno - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 8 # I was crazy once - replacement: - - s - - S - -- type: language - id: Monkey - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 8 # They locked me in a room... - replacement: - - o - - k - -- type: language - id: Mouse - obfuscation: - !type:SyllableObfuscation - minSyllables: 2 - maxSyllables: 3 - replacement: - - squ - - eak - - pi - - ep - - chuu - - ee - - fwi - - he - -- type: language - id: Chicken - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 3 - replacement: - - co - - coo - - ot - -- type: language - id: Duck - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 3 - replacement: - - qu - - ack - - quack - -- type: language - id: Cow - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 3 - replacement: - - moo - - mooo - -- type: language - id: Sheep - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 3 - replacement: - - ba - - baa - - aa - -- type: language - id: Kangaroo - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 3 - replacement: - - shre - - ack - - chuu - - choo - -- type: language - id: Pig - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 3 - replacement: - - oink # Please someone come up with something better - -- type: language - id: Crab - obfuscation: - !type:SyllableObfuscation - minSyllables: 1 - maxSyllables: 3 - replacement: - - click - - clack - - ti - - pi - - tap - - cli - - ick - -- type: language - id: Kobold - obfuscation: - !type:SyllableObfuscation - minSyllables: 2 - maxSyllables: 4 - replacement: - - yip - - yap - - gar - - grr - - ar - - scre - - et - - gronk - - hiss - - ss - - ee - -- type: language - id: Hissing - obfuscation: - !type:SyllableObfuscation - minSyllables: 2 - maxSyllables: 4 - replacement: - - hss - - iss - - ss - - is - -# Example of a sign language. Not currently used anyhow. -- type: language - id: Sign - speech: - allowRadio: false - requireSpeech: false - color: "#dddddd" - messageWrapOverrides: - Speak: chat-sign-language-message-wrap - Whisper: chat-sign-language-whisper-wrap - speechVerbOverrides: - - chat-speech-verb-sign-1 - - chat-speech-verb-sign-2 - - chat-speech-verb-sign-3 - obfuscation: - !type:ReplacementObfuscation - replacement: - - something - - a cryptic message to you - - a signal to you - - a message - - a rude expression to you - - a sad expression to you - - a happy expression to you diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/Oni.yml b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/Oni.yml index c1655287fdc..da723e7c43d 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/Oni.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/Oni.yml @@ -40,10 +40,10 @@ proto: oni - type: LanguageKnowledge speaks: - - GalacticCommon + - TauCetiBasic - Nekomimetic understands: - - GalacticCommon + - TauCetiBasic - Nekomimetic - type: entity diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml index 411eb13444b..44779fe9508 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml @@ -67,12 +67,10 @@ - type: NoShoesSilentFootsteps - type: LanguageKnowledge speaks: - - GalacticCommon - - SolCommon + - TauCetiBasic - Nekomimetic understands: - - GalacticCommon - - SolCommon + - TauCetiBasic - Nekomimetic - type: Thieving ignoreStripHidden: true diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml index 1a065b001c1..511262d3186 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml @@ -48,3 +48,10 @@ - type: GuideHelp guides: - Psionics + - type: LanguageSpeaker + currentLanguage: TauCetiBasic + - type: LanguageKnowledge + speaks: + - TauCetiBasic + understands: + - TauCetiBasic diff --git a/Resources/Prototypes/Nyanotrasen/Objectives/traitor.yml b/Resources/Prototypes/Nyanotrasen/Objectives/traitor.yml index 53910a54a92..e6e497003d5 100644 --- a/Resources/Prototypes/Nyanotrasen/Objectives/traitor.yml +++ b/Resources/Prototypes/Nyanotrasen/Objectives/traitor.yml @@ -9,29 +9,6 @@ stealGroup: AntiPsychicKnife owner: job-name-mantis -- type: entity - id: BecomePsionicObjective - parent: BaseTraitorObjective - name: Become psionic - description: We need you to acquire psionics and keep them until your mission is complete. - noSpawn: true - components: - - type: NotJobsRequirement - jobs: - - Mime - - ForensicMantis - - type: Objective - difficulty: 2.5 - #unique: false - icon: - sprite: Nyanotrasen/Icons/psi.rsi - state: psi - - type: ObjectiveBlacklistRequirement - blacklist: - components: - - BecomeGolemCondition - - type: BecomePsionicCondition - #- type: entity # id: BecomeGolemObjective # parent: BaseTraitorObjective diff --git a/Resources/Prototypes/Nyanotrasen/psionicPowers.yml b/Resources/Prototypes/Nyanotrasen/psionicPowers.yml index 0781122b8ec..2aee2273a18 100644 --- a/Resources/Prototypes/Nyanotrasen/psionicPowers.yml +++ b/Resources/Prototypes/Nyanotrasen/psionicPowers.yml @@ -11,3 +11,5 @@ # PsionicInvisibilityPower: 0.15 MindSwapPower: 0.15 TelepathyPower: 1 + HealingWordPower: 0.85 + RevivifyPower: 0.1 diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index 263494f7987..6929d1e1a47 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -40,8 +40,6 @@ EscapeShuttleObjective: 1 # DieObjective: 0.05 # DeltaV - Disable the lrp objective aka murderbone justification #HijackShuttleObjective: 0.02 - BecomePsionicObjective: 1 # Nyanotrasen - Become Psionic objective, see Resources/Prototypes/Nyanotrasen/Objectives/traitor.yml - #BecomeGolemObjective: 0.5 # Nyanotrasen - Become a golem objective, see Resources/Prototypes/Nyanotrasen/Objectives/traitor.yml - type: weightedRandom id: TraitorObjectiveGroupSocial diff --git a/Resources/Prototypes/Psionics/psionics.yml b/Resources/Prototypes/Psionics/psionics.yml index 461098eab21..b881f1f11fb 100644 --- a/Resources/Prototypes/Psionics/psionics.yml +++ b/Resources/Prototypes/Psionics/psionics.yml @@ -140,3 +140,25 @@ initializationFeedback: telepathy-power-initialization-feedback metapsionicFeedback: psionic-language-power-feedback # Reuse for telepathy, clairaudience, etc powerSlotCost: 0 + +- type: psionicPower + id: HealingWordPower + name: HealingWord + description: healing-word-power-description + actions: + - ActionHealingWord + initializationFeedback: healing-word-power-initialization-feedback + metapsionicFeedback: healing-word-power-feedback + amplificationModifier: 0.5 + dampeningModifier: 0.5 + +- type: psionicPower + id: RevivifyPower + name: Revivify + description: revivify-power-description + actions: + - ActionRevivify + initializationFeedback: revivify-power-initialization-feedback + metapsionicFeedback: revivify-power-feedback + amplificationModifier: 2.5 # An extremely rare and dangerous power + powerSlotCost: 2 diff --git a/Resources/Prototypes/Recipes/Lathes/Parts.yml b/Resources/Prototypes/Recipes/Lathes/Parts.yml index 90cff2174d6..496bc3a8a48 100644 --- a/Resources/Prototypes/Recipes/Lathes/Parts.yml +++ b/Resources/Prototypes/Recipes/Lathes/Parts.yml @@ -1,3 +1,4 @@ +#Rating 1 - type: latheRecipe id: CapacitorStockPart result: CapacitorStockPart @@ -24,3 +25,62 @@ materials: Steel: 50 Plastic: 50 + +#Rating 2 +- type: latheRecipe + id: AdvancedCapacitorStockPart + result: AdvancedCapacitorStockPart + completetime: 3 + materials: + Steel: 80 + Plastic: 80 + Plasma: 75 + +- type: latheRecipe + id: AdvancedMatterBinStockPart + result: AdvancedMatterBinStockPart + completetime: 3 + materials: + Steel: 80 + Plastic: 80 + Plasma: 75 + +- type: latheRecipe + id: NanoManipulatorStockPart + result: NanoManipulatorStockPart + completetime: 3 + materials: + Steel: 80 + Plastic: 80 + Plasma: 75 + +#Rating 3 +- type: latheRecipe + id: SuperCapacitorStockPart + result: SuperCapacitorStockPart + completetime: 3 + materials: + Steel: 150 + Plastic: 150 + Plasma: 75 + Gold: 75 + +- type: latheRecipe + id: SuperMatterBinStockPart + result: SuperMatterBinStockPart + completetime: 3 + materials: + Steel: 150 + Plastic: 150 + Plasma: 75 + Gold: 75 + +- type: latheRecipe + id: PicoManipulatorStockPart + result: PicoManipulatorStockPart + completetime: 3 + materials: + Steel: 150 + Plastic: 150 + Plasma: 75 + Gold: 75 diff --git a/Resources/Prototypes/Research/experimental.yml b/Resources/Prototypes/Research/experimental.yml index d46e1db144e..85523033f86 100644 --- a/Resources/Prototypes/Research/experimental.yml +++ b/Resources/Prototypes/Research/experimental.yml @@ -84,6 +84,20 @@ # Tier 2 +- type: technology + id: AdvancedParts + name: research-technology-advanced-parts + icon: + sprite: Objects/Misc/stock_parts.rsi + state: advanced_matter_bin + discipline: Experimental + tier: 2 + cost: 10000 + recipeUnlocks: + - AdvancedCapacitorStockPart + - AdvancedMatterBinStockPart + - NanoManipulatorStockPart + - type: technology id: AbnormalArtifactManipulation name: research-technology-abnormal-artifact-manipulation @@ -155,6 +169,20 @@ # Tier 3 +- type: technology + id: SuperParts + name: research-technology-super-parts + icon: + sprite: Objects/Misc/stock_parts.rsi + state: super_matter_bin + discipline: Experimental + tier: 3 + cost: 15000 + recipeUnlocks: + - SuperCapacitorStockPart + - SuperMatterBinStockPart + - PicoManipulatorStockPart + #- type: technology # DeltaV - LRP # id: GravityManipulation # name: research-technology-gravity-manipulation diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml index c7395ff3e2d..d4f8bdb0671 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml @@ -5,8 +5,8 @@ playTimeTracker: JobChaplain requirements: - !type:CharacterDepartmentTimeRequirement - department: Epistemics # DeltaV - Epistemics Department replacing Science - min: 14400 #DeltaV 4 hours + department: Epistemics # Chaplain is now one of the station's "Crew-Aligned Wizards" + min: 14400 # 4 hours - !type:CharacterLogicOrRequirement requirements: - !type:CharacterSpeciesRequirement @@ -21,7 +21,7 @@ supervisors: job-supervisors-rd access: - Chapel - - Research # DeltaV - Move Chaplain into Epistemics + - Research - Maintenance special: - !type:AddComponentSpecial @@ -32,6 +32,7 @@ - type: InnatePsionicPowers powersToAdd: - TelepathyPower + - HealingWordPower - type: startingGear id: ChaplainGear @@ -40,7 +41,7 @@ back: ClothingBackpackChaplainFilled shoes: ClothingShoesColorBlack id: ChaplainPDA - ears: ClothingHeadsetScience # DeltaV - Move Chaplain into Epistemics + ears: ClothingHeadsetScience innerClothingSkirt: ClothingUniformJumpskirtChaplain satchel: ClothingBackpackSatchelChaplainFilled duffelbag: ClothingBackpackDuffelChaplainFilled diff --git a/Resources/Prototypes/Store/presets.yml b/Resources/Prototypes/Store/presets.yml index e623f4c8cd6..bbf96997956 100644 --- a/Resources/Prototypes/Store/presets.yml +++ b/Resources/Prototypes/Store/presets.yml @@ -13,5 +13,13 @@ - UplinkJob - UplinkArmor - UplinkPointless + - UplinkSales currencyWhitelist: - Telecrystal + sales: + enabled: true + minMultiplier: 0.2 + maxMultiplier: 0.8 + minItems: 3 + maxItems: 8 + salesCategory: UplinkSales diff --git a/Resources/Prototypes/Traits/categories.yml b/Resources/Prototypes/Traits/categories.yml index e413706bfa0..fcd89d5bbe8 100644 --- a/Resources/Prototypes/Traits/categories.yml +++ b/Resources/Prototypes/Traits/categories.yml @@ -19,7 +19,24 @@ - type: traitCategory id: Speech root: true + subCategories: + - TraitsSpeechUncategorized + - TraitsSpeechAccents + - TraitsSpeechLanguages + +- type: traitCategory + id: TraitsSpeechUncategorized + +- type: traitCategory + id: TraitsSpeechAccents + +- type: traitCategory + id: TraitsSpeechLanguages - type: traitCategory id: Visual root: true + +- type: traitCategory + id: Language + root: true \ No newline at end of file diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index 3afc3bfba43..2b37aadb9a9 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -51,7 +51,7 @@ - type: trait id: Muted - category: Speech + category: Mental points: 4 requirements: - !type:CharacterJobRequirement @@ -77,7 +77,7 @@ - type: trait id: FrontalLisp - category: Speech + category: TraitsSpeechAccents requirements: - !type:CharacterJobRequirement inverted: true @@ -88,6 +88,8 @@ inverted: true species: - IPC + - !type:CharacterItemGroupRequirement + group: TraitsAccents components: - type: FrontalLisp diff --git a/Resources/Prototypes/Traits/inconveniences.yml b/Resources/Prototypes/Traits/inconveniences.yml index b08116dc480..2c0df79264f 100644 --- a/Resources/Prototypes/Traits/inconveniences.yml +++ b/Resources/Prototypes/Traits/inconveniences.yml @@ -23,13 +23,15 @@ - type: trait id: Stutter - category: Mental + category: TraitsSpeechAccents requirements: - !type:CharacterJobRequirement inverted: true jobs: - Borg - MedicalBorg + - !type:CharacterItemGroupRequirement + group: TraitsAccents components: - type: StutteringAccent matchRandomProb: 0.1 @@ -39,7 +41,7 @@ - type: trait id: ForeignerLight - category: Mental + category: TraitsSpeechLanguages points: 2 requirements: - !type:CharacterTraitRequirement @@ -53,7 +55,7 @@ - type: trait id: Foreigner - category: Mental + category: TraitsSpeechLanguages points: 4 requirements: # TODO: Add a requirement to know at least 1 non-gc language - !type:CharacterTraitRequirement diff --git a/Resources/Prototypes/Traits/languages.yml b/Resources/Prototypes/Traits/languages.yml new file mode 100644 index 00000000000..5fc1f3bb918 --- /dev/null +++ b/Resources/Prototypes/Traits/languages.yml @@ -0,0 +1,67 @@ +- type: trait + id: SignLanguage + category: TraitsSpeechLanguages + points: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: TraitsLanguagesBasic + languagesSpoken: + - Sign + languagesUnderstood: + - Sign + +- type: trait + id: SolCommon + category: TraitsSpeechLanguages + points: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: TraitsLanguagesBasic + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Human + languagesSpoken: + - SolCommon + languagesUnderstood: + - SolCommon + +- type: trait + id: Tradeband + category: TraitsSpeechLanguages + points: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: TraitsLanguagesBasic + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Harpy + languagesSpoken: + - Tradeband + languagesUnderstood: + - Tradeband + +- type: trait + id: Freespeak + category: TraitsSpeechLanguages + points: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: TraitsLanguagesBasic + languagesSpoken: + - Freespeak + languagesUnderstood: + - Freespeak + +- type: trait + id: Elyran + category: TraitsSpeechLanguages + points: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: TraitsLanguagesBasic + languagesSpoken: + - Elyran + languagesUnderstood: + - Elyran \ No newline at end of file diff --git a/Resources/Prototypes/Traits/neutral.yml b/Resources/Prototypes/Traits/neutral.yml index a103f5abeed..6186c3382b7 100644 --- a/Resources/Prototypes/Traits/neutral.yml +++ b/Resources/Prototypes/Traits/neutral.yml @@ -1,19 +1,24 @@ - type: trait id: PirateAccent - category: Speech + category: TraitsSpeechAccents + requirements: + - !type:CharacterItemGroupRequirement + group: TraitsAccents components: - type: PirateAccent - type: trait id: Accentless - category: Speech - points: -2 + category: TraitsSpeechAccents + points: -1 requirements: - !type:CharacterJobRequirement inverted: true jobs: - Borg - MedicalBorg + - !type:CharacterItemGroupRequirement + group: TraitsAccents components: - type: Accentless removes: @@ -24,7 +29,10 @@ - type: trait id: Southern - category: Speech + category: TraitsSpeechAccents + requirements: + - !type:CharacterItemGroupRequirement + group: TraitsAccents components: - type: SouthernAccent diff --git a/Resources/Prototypes/Traits/skills.yml b/Resources/Prototypes/Traits/skills.yml index fa79666c7ab..0e4868f19bc 100644 --- a/Resources/Prototypes/Traits/skills.yml +++ b/Resources/Prototypes/Traits/skills.yml @@ -98,17 +98,6 @@ species: - Felinid -- type: trait - id: SignLanguage - category: Visual - points: -2 - components: - - type: LanguageKnowledgeModifier - speaks: - - Sign - understands: - - Sign - - type: trait id: Voracious category: Physical diff --git a/Resources/Prototypes/XenoArch/Effects/normal_effects.yml b/Resources/Prototypes/XenoArch/Effects/normal_effects.yml index b9564c0366b..0a0b9bcbcc3 100644 --- a/Resources/Prototypes/XenoArch/Effects/normal_effects.yml +++ b/Resources/Prototypes/XenoArch/Effects/normal_effects.yml @@ -584,6 +584,24 @@ messages: - shuffle-artifact-popup +- type: artifactEffect + id: EffectT4PartsSpawn + targetDepth: 3 + effectHint: artifact-effect-hint-creation + components: + - type: SpawnArtifact + maxSpawns: 10 + spawns: + - id: QuadraticCapacitorStockPart + prob: 0.5 + maxAmount: 3 + - id: FemtoManipulatorStockPart + prob: 0.5 + maxAmount: 3 + - id: BluespaceMatterBinStockPart + prob: 0.5 + maxAmount: 3 + - type: artifactEffect id: EffectFoamDangerous targetDepth: 3 diff --git a/Resources/Prototypes/_White/Store/categories.yml b/Resources/Prototypes/_White/Store/categories.yml new file mode 100644 index 00000000000..cb9cfbc88f8 --- /dev/null +++ b/Resources/Prototypes/_White/Store/categories.yml @@ -0,0 +1,4 @@ +- type: storeCategory + id: UplinkSales + name: Sales + priority: 10 diff --git a/Resources/ServerInfo/Guidebook/Science/MachineUpgrading.xml b/Resources/ServerInfo/Guidebook/Science/MachineUpgrading.xml new file mode 100644 index 00000000000..286219b4d9e --- /dev/null +++ b/Resources/ServerInfo/Guidebook/Science/MachineUpgrading.xml @@ -0,0 +1,38 @@ + +# Machine Upgrading + +Machines help the station run smoothly, and as a scientist, you can help them run even better! + +## Parts +Stock Parts: + + + + + +You can examine each machine part to see both the type and the rating, which range from 1 to 4. + +Parts of higher levels can be researched as well as found through artifacts or salvage. + + + + + + +## Upgrading +To know if a machine can be upgraded, you can examine it and check for the [color=#a4885c]lightning bolt[/color] icon in the lower right corner. Clicking on it will allow you to see what kinds of upgrades the machine has. + +To check what parts a machine needs, you can examine its board. Try it here: + + + + + + +You can use any rating part for any part requirement. Using higher rated parts will increase how effective the machine is. + +If you want to upgrade an existing machine, simply deconstruct it with a screwdriver and crowbar, and replace the existing parts with parts of a higher level. + +You can also quickly upgrade machines by using an RPED, loading it with machine parts, and then clicking on a machine. It will quickly be upgraded with whatever parts were inserted. + + diff --git a/Resources/Textures/Interface/Actions/psionics.rsi/healing_word.png b/Resources/Textures/Interface/Actions/psionics.rsi/healing_word.png new file mode 100644 index 00000000000..800e2a46e61 Binary files /dev/null and b/Resources/Textures/Interface/Actions/psionics.rsi/healing_word.png differ diff --git a/Resources/Textures/Interface/Actions/psionics.rsi/meta.json b/Resources/Textures/Interface/Actions/psionics.rsi/meta.json new file mode 100644 index 00000000000..085cc412814 --- /dev/null +++ b/Resources/Textures/Interface/Actions/psionics.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by leonardo_dabepis (discord)", + "size": { + "x": 64, + "y": 64 + }, + "states": [ + { + "name": "healing_word" + }, + { + "name": "revivify" + } + ] +} diff --git a/Resources/Textures/Interface/Actions/psionics.rsi/revivify.png b/Resources/Textures/Interface/Actions/psionics.rsi/revivify.png new file mode 100644 index 00000000000..087f40b4c98 Binary files /dev/null and b/Resources/Textures/Interface/Actions/psionics.rsi/revivify.png differ diff --git a/Resources/Textures/Interface/AtmosMonitoring/status_bg.png b/Resources/Textures/Interface/AtmosMonitoring/status_bg.png new file mode 100644 index 00000000000..165a9b9d9f1 Binary files /dev/null and b/Resources/Textures/Interface/AtmosMonitoring/status_bg.png differ diff --git a/Resources/Textures/Interface/VerbIcons/ATTRIBUTION.txt b/Resources/Textures/Interface/VerbIcons/ATTRIBUTION.txt index 0b8ae856bd8..69aa4650b32 100644 --- a/Resources/Textures/Interface/VerbIcons/ATTRIBUTION.txt +++ b/Resources/Textures/Interface/VerbIcons/ATTRIBUTION.txt @@ -7,3 +7,6 @@ https://game-icons.net/1x1/lorc/padlock.html unlock.svg by Delapouite under CC BY 3.0 https://game-icons.net/1x1/delapouite/padlock-open.html + +healing_word.png by LeonardoDaBepis under CC BY 3.0 +revivify.png by LeonardoDaBepis under CC BY 3.0 \ No newline at end of file