Merge remote-tracking branch 'upstream/master' into 20-10-30-admins

This commit is contained in:
Pieter-Jan Briers
2020-11-10 16:59:17 +01:00
473 changed files with 5588 additions and 3584 deletions

View File

@@ -13,6 +13,7 @@ using Content.Client.UserInterface.AdminMenu;
using Content.Client.UserInterface.Stylesheets; using Content.Client.UserInterface.Stylesheets;
using Content.Client.Utility; using Content.Client.Utility;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
using Content.Shared.Alert;
using Robust.Shared.IoC; using Robust.Shared.IoC;
namespace Content.Client namespace Content.Client
@@ -37,6 +38,7 @@ namespace Content.Client
IoCManager.Register<IClickMapManager, ClickMapManager>(); IoCManager.Register<IClickMapManager, ClickMapManager>();
IoCManager.Register<IStationEventManager, StationEventManager>(); IoCManager.Register<IStationEventManager, StationEventManager>();
IoCManager.Register<IAdminMenuManager, AdminMenuManager>(); IoCManager.Register<IAdminMenuManager, AdminMenuManager>();
IoCManager.Register<AlertManager, AlertManager>();
IoCManager.Register<IClientAdminManager, ClientAdminManager>(); IoCManager.Register<IClientAdminManager, ClientAdminManager>();
IoCManager.Register<EuiManager, EuiManager>(); IoCManager.Register<EuiManager, EuiManager>();
} }

View File

@@ -1,3 +1,4 @@
using Content.Shared;
using Robust.Client.Interfaces.Console; using Robust.Client.Interfaces.Console;
using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.IoC; using Robust.Shared.IoC;
@@ -14,10 +15,13 @@ namespace Content.Client.Commands
public bool Execute(IDebugConsole console, params string[] args) public bool Execute(IDebugConsole console, params string[] args)
{ {
var _configurationManager = IoCManager.Resolve<IConfigurationManager>(); var configurationManager = IoCManager.Resolve<IConfigurationManager>();
var old = _configurationManager.GetCVar<bool>("outline.enabled"); var cvar = CCVars.OutlineEnabled;
_configurationManager.SetCVar("outline.enabled", !old); var old = configurationManager.GetCVar(cvar);
console.AddLine($"Draw outlines set to: {_configurationManager.GetCVar<bool>("outline.enabled")}");
configurationManager.SetCVar(cvar, !old);
console.AddLine($"Draw outlines set to: {configurationManager.GetCVar(cvar)}");
return false; return false;
} }
} }

View File

@@ -24,6 +24,7 @@ using Content.Shared.GameObjects.Components.Power.AME;
using Content.Shared.GameObjects.Components.Research; using Content.Shared.GameObjects.Components.Research;
using Content.Shared.GameObjects.Components.VendingMachines; using Content.Shared.GameObjects.Components.VendingMachines;
using Content.Shared.Kitchen; using Content.Shared.Kitchen;
using Content.Shared.Alert;
using Robust.Client; using Robust.Client;
using Robust.Client.Interfaces; using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Graphics.Overlays; using Robust.Client.Interfaces.Graphics.Overlays;
@@ -104,8 +105,6 @@ namespace Content.Client
{ {
IoCManager.Resolve<IMapManager>().CreateNewMapEntity(MapId.Nullspace); IoCManager.Resolve<IMapManager>().CreateNewMapEntity(MapId.Nullspace);
}; };
_configurationManager.RegisterCVar("outline.enabled", true);
} }
/// <summary> /// <summary>
@@ -156,6 +155,7 @@ namespace Content.Client
IoCManager.Resolve<IStationEventManager>().Initialize(); IoCManager.Resolve<IStationEventManager>().Initialize();
IoCManager.Resolve<IAdminMenuManager>().Initialize(); IoCManager.Resolve<IAdminMenuManager>().Initialize();
IoCManager.Resolve<EuiManager>().Initialize(); IoCManager.Resolve<EuiManager>().Initialize();
IoCManager.Resolve<AlertManager>().Initialize();
_baseClient.RunLevelChanged += (sender, args) => _baseClient.RunLevelChanged += (sender, args) =>
{ {

View File

@@ -1,12 +0,0 @@
using Content.Shared.GameObjects.Components.Body.Behavior;
using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Body.Mechanism
{
[RegisterComponent]
[ComponentReference(typeof(SharedHeartBehaviorComponent))]
public class HeartBehaviorComponent : SharedHeartBehaviorComponent
{
public override void Update(float frameTime) { }
}
}

View File

@@ -49,6 +49,7 @@ namespace Content.Client.GameObjects.Components.Wires
{ {
base.Dispose(disposing); base.Dispose(disposing);
_menu.OnClose -= Close;
_menu.Close(); _menu.Close();
} }
} }

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Namotion.Reflection;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
@@ -142,7 +141,7 @@ namespace Content.Client.GameObjects.Components.Wires
private void OnConfirm(ButtonEventArgs args) private void OnConfirm(ButtonEventArgs args)
{ {
var config = GenerateDictionary<string, LineEdit>(_inputs, "Text"); var config = GenerateDictionary(_inputs, "Text");
Owner.SendConfiguration(config); Owner.SendConfiguration(config);
Close(); Close();
@@ -153,13 +152,13 @@ namespace Content.Client.GameObjects.Components.Wires
return Owner.Validation == null || Owner.Validation.IsMatch(value); return Owner.Validation == null || Owner.Validation.IsMatch(value);
} }
private Dictionary<string, TConfig> GenerateDictionary<TConfig, TInput>(List<(string name, TInput input)> inputs, string propertyName) where TInput : Control private Dictionary<string, string> GenerateDictionary(IEnumerable<(string name, LineEdit input)> inputs, string propertyName)
{ {
var dictionary = new Dictionary<string, TConfig>(); var dictionary = new Dictionary<string, string>();
foreach (var input in inputs) foreach (var input in inputs)
{ {
var value = input.input.TryGetPropertyValue<TConfig>(propertyName); dictionary.Add(input.name, input.input.Text);
dictionary.Add(input.name, value);
} }
return dictionary; return dictionary;

View File

@@ -0,0 +1,93 @@
#nullable enable
using System;
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.Alert;
using OpenToolkit.Mathematics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.GameObjects.Components.Mobs
{
public class AlertControl : BaseButton
{
public AlertPrototype Alert { get; }
/// <summary>
/// Total duration of the cooldown in seconds. Null if no duration / cooldown.
/// </summary>
public int? TotalDuration { get; set; }
private short? _severity;
private readonly TextureRect _icon;
private CooldownGraphic _cooldownGraphic;
private readonly IResourceCache _resourceCache;
/// <summary>
/// Creates an alert control reflecting the indicated alert + state
/// </summary>
/// <param name="alert">alert to display</param>
/// <param name="severity">severity of alert, null if alert doesn't have severity levels</param>
/// <param name="resourceCache">resourceCache to use to load alert icon textures</param>
public AlertControl(AlertPrototype alert, short? severity, IResourceCache resourceCache)
{
_resourceCache = resourceCache;
Alert = alert;
_severity = severity;
var texture = _resourceCache.GetTexture(alert.GetIconPath(_severity));
_icon = new TextureRect
{
TextureScale = (2, 2),
Texture = texture
};
Children.Add(_icon);
_cooldownGraphic = new CooldownGraphic();
Children.Add(_cooldownGraphic);
}
/// <summary>
/// Change the alert severity, changing the displayed icon
/// </summary>
public void SetSeverity(short? severity)
{
if (_severity != severity)
{
_severity = severity;
_icon.Texture = _resourceCache.GetTexture(Alert.GetIconPath(_severity));
}
}
/// <summary>
/// Updates the displayed cooldown amount, doing nothing if alertCooldown is null
/// </summary>
/// <param name="alertCooldown">cooldown start and end</param>
/// <param name="curTime">current game time</param>
public void UpdateCooldown((TimeSpan Start, TimeSpan End)? alertCooldown, in TimeSpan curTime)
{
if (!alertCooldown.HasValue)
{
_cooldownGraphic.Progress = 0;
_cooldownGraphic.Visible = false;
TotalDuration = null;
}
else
{
var start = alertCooldown.Value.Start;
var end = alertCooldown.Value.End;
var length = (end - start).TotalSeconds;
var progress = (curTime - start).TotalSeconds / length;
var ratio = (progress <= 1 ? (1 - progress) : (curTime - end).TotalSeconds * -5);
TotalDuration = (int?) Math.Round(length);
_cooldownGraphic.Progress = MathHelper.Clamp((float)ratio, -1, 1);
_cooldownGraphic.Visible = ratio > -1f;
}
}
}
}

View File

@@ -0,0 +1,345 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Client.UserInterface;
using Content.Client.UserInterface.Stylesheets;
using Content.Client.Utility;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using Serilog;
namespace Content.Client.GameObjects.Components.Mobs
{
/// <inheritdoc/>
[RegisterComponent]
[ComponentReference(typeof(SharedAlertsComponent))]
public sealed class ClientAlertsComponent : SharedAlertsComponent
{
private static readonly float TooltipTextMaxWidth = 265;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
private AlertsUI _ui;
private PanelContainer _tooltip;
private RichTextLabel _stateName;
private RichTextLabel _stateDescription;
private RichTextLabel _stateCooldown;
private AlertOrderPrototype _alertOrder;
private bool _tooltipReady;
[ViewVariables]
private Dictionary<AlertKey, AlertControl> _alertControls
= new Dictionary<AlertKey, AlertControl>();
/// <summary>
/// Allows calculating if we need to act due to this component being controlled by the current mob
/// TODO: should be revisited after space-wizards/RobustToolbox#1255
/// </summary>
[ViewVariables]
private bool CurrentlyControlled => _playerManager.LocalPlayer != null && _playerManager.LocalPlayer.ControlledEntity == Owner;
protected override void Shutdown()
{
base.Shutdown();
PlayerDetached();
}
public override void HandleMessage(ComponentMessage message, IComponent component)
{
base.HandleMessage(message, component);
switch (message)
{
case PlayerAttachedMsg _:
PlayerAttached();
break;
case PlayerDetachedMsg _:
PlayerDetached();
break;
}
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is AlertsComponentState state))
{
return;
}
// update the dict of states based on the array we got in the message
SetAlerts(state.Alerts);
UpdateAlertsControls();
}
private void PlayerAttached()
{
if (!CurrentlyControlled || _ui != null)
{
return;
}
_alertOrder = IoCManager.Resolve<IPrototypeManager>().EnumeratePrototypes<AlertOrderPrototype>().FirstOrDefault();
if (_alertOrder == null)
{
Logger.ErrorS("alert", "no alertOrder prototype found, alerts will be in random order");
}
_ui = new AlertsUI(IoCManager.Resolve<IClyde>());
var uiManager = IoCManager.Resolve<IUserInterfaceManager>();
uiManager.StateRoot.AddChild(_ui);
_tooltip = new PanelContainer
{
Visible = false,
StyleClasses = { StyleNano.StyleClassTooltipPanel }
};
var tooltipVBox = new VBoxContainer
{
RectClipContent = true
};
_tooltip.AddChild(tooltipVBox);
_stateName = new RichTextLabel
{
MaxWidth = TooltipTextMaxWidth,
StyleClasses = { StyleNano.StyleClassTooltipAlertTitle }
};
tooltipVBox.AddChild(_stateName);
_stateDescription = new RichTextLabel
{
MaxWidth = TooltipTextMaxWidth,
StyleClasses = { StyleNano.StyleClassTooltipAlertDescription }
};
tooltipVBox.AddChild(_stateDescription);
_stateCooldown = new RichTextLabel
{
MaxWidth = TooltipTextMaxWidth,
StyleClasses = { StyleNano.StyleClassTooltipAlertCooldown }
};
tooltipVBox.AddChild(_stateCooldown);
uiManager.PopupRoot.AddChild(_tooltip);
UpdateAlertsControls();
}
private void PlayerDetached()
{
_ui?.Dispose();
_ui = null;
_alertControls.Clear();
}
/// <summary>
/// Updates the displayed alerts based on current state of Alerts, performing
/// a diff to ensure we only change what's changed (this avoids active tooltips disappearing any
/// time state changes)
/// </summary>
private void UpdateAlertsControls()
{
if (!CurrentlyControlled || _ui == null)
{
return;
}
// remove any controls with keys no longer present
var toRemove = new List<AlertKey>();
foreach (var existingKey in _alertControls.Keys)
{
if (!IsShowingAlert(existingKey))
{
toRemove.Add(existingKey);
}
}
foreach (var alertKeyToRemove in toRemove)
{
// remove and dispose the control
_alertControls.Remove(alertKeyToRemove, out var control);
control?.Dispose();
}
// now we know that alertControls contains alerts that should still exist but
// may need to updated,
// also there may be some new alerts we need to show.
// further, we need to ensure they are ordered w.r.t their configured order
foreach (var alertStatus in EnumerateAlertStates())
{
if (!AlertManager.TryDecode(alertStatus.AlertEncoded, out var newAlert))
{
Logger.ErrorS("alert", "Unable to decode alert {0}", alertStatus.AlertEncoded);
continue;
}
if (_alertControls.TryGetValue(newAlert.AlertKey, out var existingAlertControl) &&
existingAlertControl.Alert.AlertType == newAlert.AlertType)
{
// id is the same, simply update the existing control severity
existingAlertControl.SetSeverity(alertStatus.Severity);
}
else
{
existingAlertControl?.Dispose();
// this is a new alert + alert key or just a different alert with the same
// key, create the control and add it in the appropriate order
var newAlertControl = CreateAlertControl(newAlert, alertStatus);
if (_alertOrder != null)
{
var added = false;
foreach (var alertControl in _ui.Grid.Children)
{
if (_alertOrder.Compare(newAlert, ((AlertControl) alertControl).Alert) < 0)
{
var idx = alertControl.GetPositionInParent();
_ui.Grid.Children.Add(newAlertControl);
newAlertControl.SetPositionInParent(idx);
added = true;
break;
}
}
if (!added)
{
_ui.Grid.Children.Add(newAlertControl);
}
}
else
{
_ui.Grid.Children.Add(newAlertControl);
}
_alertControls[newAlert.AlertKey] = newAlertControl;
}
}
}
private AlertControl CreateAlertControl(AlertPrototype alert, AlertState alertState)
{
var alertControl = new AlertControl(alert, alertState.Severity, _resourceCache);
// show custom tooltip for the status control
alertControl.OnShowTooltip += AlertOnOnShowTooltip;
alertControl.OnHideTooltip += AlertOnOnHideTooltip;
alertControl.OnPressed += AlertControlOnPressed;
return alertControl;
}
private void AlertControlOnPressed(BaseButton.ButtonEventArgs args)
{
AlertPressed(args, args.Button as AlertControl);
}
private void AlertOnOnHideTooltip(object sender, EventArgs e)
{
_tooltipReady = false;
_tooltip.Visible = false;
}
private void AlertOnOnShowTooltip(object sender, EventArgs e)
{
var alertControl = (AlertControl) sender;
_stateName.SetMessage(alertControl.Alert.Name);
_stateDescription.SetMessage(alertControl.Alert.Description);
// check for a cooldown
if (alertControl.TotalDuration != null && alertControl.TotalDuration > 0)
{
_stateCooldown.SetMessage(FormattedMessage.FromMarkup("[color=#776a6a]" +
alertControl.TotalDuration +
" sec cooldown[/color]"));
_stateCooldown.Visible = true;
}
else
{
_stateCooldown.Visible = false;
}
// TODO: Text display of cooldown
Tooltips.PositionTooltip(_tooltip);
// if we set it visible here the size of the previous tooltip will flicker for a frame,
// so instead we wait until FrameUpdate to make it visible
_tooltipReady = true;
}
private void AlertPressed(BaseButton.ButtonEventArgs args, AlertControl alert)
{
if (args.Event.Function != EngineKeyFunctions.UIClick)
{
return;
}
if (AlertManager.TryEncode(alert.Alert, out var encoded))
{
SendNetworkMessage(new ClickAlertMessage(encoded));
}
else
{
Logger.ErrorS("alert", "unable to encode alert {0}", alert.Alert.AlertType);
}
}
public void FrameUpdate(float frameTime)
{
if (_tooltipReady)
{
_tooltipReady = false;
_tooltip.Visible = true;
}
foreach (var (alertKey, alertControl) in _alertControls)
{
// reconcile all alert controls with their current cooldowns
if (TryGetAlertState(alertKey, out var alertState))
{
alertControl.UpdateCooldown(alertState.Cooldown, _gameTiming.CurTime);
}
else
{
Logger.WarningS("alert", "coding error - no alert state for alert {0} " +
"even though we had an AlertControl for it, this" +
" should never happen", alertControl.Alert.AlertType);
}
}
}
protected override void AfterClearAlert()
{
UpdateAlertsControls();
}
public override void OnRemove()
{
base.OnRemove();
foreach (var alertControl in _alertControls.Values)
{
alertControl.OnShowTooltip -= AlertOnOnShowTooltip;
alertControl.OnHideTooltip -= AlertOnOnHideTooltip;
alertControl.OnPressed -= AlertControlOnPressed;
}
}
}
}

View File

@@ -1,200 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Mobs
{
/// <inheritdoc/>
[RegisterComponent]
[ComponentReference(typeof(SharedStatusEffectsComponent))]
public sealed class ClientStatusEffectsComponent : SharedStatusEffectsComponent
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
private StatusEffectsUI _ui;
[ViewVariables]
private Dictionary<StatusEffect, StatusEffectStatus> _status = new Dictionary<StatusEffect, StatusEffectStatus>();
[ViewVariables]
private Dictionary<StatusEffect, CooldownGraphic> _cooldown = new Dictionary<StatusEffect, CooldownGraphic>();
public override IReadOnlyDictionary<StatusEffect, StatusEffectStatus> Statuses => _status;
/// <summary>
/// Allows calculating if we need to act due to this component being controlled by the current mob
/// </summary>
[ViewVariables]
private bool CurrentlyControlled => _playerManager.LocalPlayer != null && _playerManager.LocalPlayer.ControlledEntity == Owner;
protected override void Shutdown()
{
base.Shutdown();
PlayerDetached();
}
public override void HandleMessage(ComponentMessage message, IComponent component)
{
base.HandleMessage(message, component);
switch (message)
{
case PlayerAttachedMsg _:
PlayerAttached();
break;
case PlayerDetachedMsg _:
PlayerDetached();
break;
}
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is StatusEffectComponentState state) || _status == state.StatusEffects)
{
return;
}
_status = state.StatusEffects;
UpdateStatusEffects();
}
private void PlayerAttached()
{
if (!CurrentlyControlled || _ui != null)
{
return;
}
_ui = new StatusEffectsUI();
_userInterfaceManager.StateRoot.AddChild(_ui);
UpdateStatusEffects();
}
private void PlayerDetached()
{
_ui?.Dispose();
_ui = null;
_cooldown.Clear();
}
public override void ChangeStatusEffectIcon(StatusEffect effect, string icon)
{
if (_status.TryGetValue(effect, out var value) &&
value.Icon == icon)
{
return;
}
_status[effect] = new StatusEffectStatus
{
Icon = icon,
Cooldown = value.Cooldown
};
Dirty();
}
public void UpdateStatusEffects()
{
if (!CurrentlyControlled || _ui == null)
{
return;
}
_cooldown.Clear();
_ui.VBox.DisposeAllChildren();
foreach (var (key, effect) in _status.OrderBy(x => (int) x.Key))
{
var texture = _resourceCache.GetTexture(effect.Icon);
var status = new StatusControl(key, texture)
{
ToolTip = key.ToString()
};
if (effect.Cooldown.HasValue)
{
var cooldown = new CooldownGraphic();
status.Children.Add(cooldown);
_cooldown[key] = cooldown;
}
status.OnPressed += args => StatusPressed(args, status);
_ui.VBox.AddChild(status);
}
}
private void StatusPressed(BaseButton.ButtonEventArgs args, StatusControl status)
{
if (args.Event.Function != EngineKeyFunctions.UIClick)
{
return;
}
SendNetworkMessage(new ClickStatusMessage(status.Effect));
}
public override void RemoveStatusEffect(StatusEffect effect)
{
if (!_status.Remove(effect))
{
return;
}
UpdateStatusEffects();
Dirty();
}
public void FrameUpdate(float frameTime)
{
foreach (var (effect, cooldownGraphic) in _cooldown)
{
var status = _status[effect];
if (!status.Cooldown.HasValue)
{
cooldownGraphic.Progress = 0;
cooldownGraphic.Visible = false;
continue;
}
var start = status.Cooldown.Value.Item1;
var end = status.Cooldown.Value.Item2;
var length = (end - start).TotalSeconds;
var progress = (_gameTiming.CurTime - start).TotalSeconds / length;
var ratio = (progress <= 1 ? (1 - progress) : (_gameTiming.CurTime - end).TotalSeconds * -5);
cooldownGraphic.Progress = MathHelper.Clamp((float)ratio, -1, 1);
cooldownGraphic.Visible = ratio > -1f;
}
}
public override void ChangeStatusEffect(StatusEffect effect, string icon, (TimeSpan, TimeSpan)? cooldown)
{
_status[effect] = new StatusEffectStatus()
{
Icon = icon,
Cooldown = cooldown
};
Dirty();
}
}
}

View File

@@ -1,4 +1,4 @@
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components; using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Maths; using Robust.Shared.Maths;
@@ -39,15 +39,6 @@ namespace Content.Client.GameObjects.Components.Mobs
offset *= (ResetTime - _time) / ResetTime; offset *= (ResetTime - _time) / ResetTime;
} }
if (Owner.TryGetComponent(out CameraRecoilComponent recoilComponent))
{
recoilComponent.BaseOffset = offset;
}
else if (Owner.TryGetComponent(out EyeComponent eyeComponent))
{
eyeComponent.Offset = offset;
}
if (Owner.TryGetComponent(out ISpriteComponent spriteComponent)) if (Owner.TryGetComponent(out ISpriteComponent spriteComponent))
{ {
// We have to account for rotation so the offset still checks out. // We have to account for rotation so the offset still checks out.

View File

@@ -1,25 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.GameObjects.Components.Mobs
{
public class StatusControl : BaseButton
{
public readonly StatusEffect Effect;
public StatusControl(StatusEffect effect, Texture? texture)
{
Effect = effect;
var item = new TextureRect
{
TextureScale = (2, 2),
Texture = texture
};
Children.Add(item);
}
}
}

View File

@@ -44,12 +44,14 @@ namespace Content.Client.GameObjects.Components.Storage
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite)) return; if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite)) return;
sprite.LayerSetState( if (component.TryGetData(MorgueVisuals.Open, out bool open))
CrematoriumVisualLayers.Base, {
component.GetData<bool>(MorgueVisuals.Open) sprite.LayerSetState(CrematoriumVisualLayers.Base, open ? _stateOpen : _stateClosed);
? _stateOpen }
: _stateClosed else
); {
sprite.LayerSetState(CrematoriumVisualLayers.Base, _stateClosed);
}
var lightState = ""; var lightState = "";
if (component.TryGetData(MorgueVisuals.HasContents, out bool hasContents) && hasContents) lightState = _lightContents; if (component.TryGetData(MorgueVisuals.HasContents, out bool hasContents) && hasContents) lightState = _lightContents;

View File

@@ -49,12 +49,14 @@ namespace Content.Client.GameObjects.Components.Storage
if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite)) return; if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite)) return;
sprite.LayerSetState( if (component.TryGetData(MorgueVisuals.Open, out bool open))
MorgueVisualLayers.Base, {
component.GetData<bool>(MorgueVisuals.Open) sprite.LayerSetState(MorgueVisualLayers.Base, open ? _stateOpen : _stateClosed);
? _stateOpen }
: _stateClosed else
); {
sprite.LayerSetState(MorgueVisualLayers.Base, _stateClosed);
}
var lightState = ""; var lightState = "";
if (component.TryGetData(MorgueVisuals.HasContents, out bool hasContents) && hasContents) lightState = _lightContents; if (component.TryGetData(MorgueVisuals.HasContents, out bool hasContents) && hasContents) lightState = _lightContents;

View File

@@ -23,7 +23,7 @@ namespace Content.Client.GameObjects.Components.PDA
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
private PDAMenu _menu; private PDAMenu _menu;
private PDAMenuPopup failPopup; private PDAMenuPopup _failPopup;
public PDABoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) public PDABoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{ {
@@ -47,6 +47,11 @@ namespace Content.Client.GameObjects.Components.PDA
SendMessage(new PDAEjectIDMessage()); SendMessage(new PDAEjectIDMessage());
}; };
_menu.EjectPenButton.OnPressed += args =>
{
SendMessage(new PDAEjectPenMessage());
};
_menu.MasterTabContainer.OnTabChanged += i => _menu.MasterTabContainer.OnTabChanged += i =>
{ {
var tab = _menu.MasterTabContainer.GetChild(i); var tab = _menu.MasterTabContainer.GetChild(i);
@@ -60,12 +65,12 @@ namespace Content.Client.GameObjects.Components.PDA
{ {
if (_menu.CurrentLoggedInAccount.DataBalance < listing.Price) if (_menu.CurrentLoggedInAccount.DataBalance < listing.Price)
{ {
failPopup = new PDAMenuPopup(Loc.GetString("Insufficient funds!")); _failPopup = new PDAMenuPopup(Loc.GetString("Insufficient funds!"));
_userInterfaceManager.ModalRoot.AddChild(failPopup); _userInterfaceManager.ModalRoot.AddChild(_failPopup);
failPopup.Open(UIBox2.FromDimensions(_menu.Position.X + 150, _menu.Position.Y + 60, 156, 24)); _failPopup.Open(UIBox2.FromDimensions(_menu.Position.X + 150, _menu.Position.Y + 60, 156, 24));
_menu.OnClose += () => _menu.OnClose += () =>
{ {
failPopup.Dispose(); _failPopup.Dispose();
}; };
} }
@@ -106,6 +111,7 @@ namespace Content.Client.GameObjects.Components.PDA
} }
_menu.EjectIDButton.Visible = msg.PDAOwnerInfo.IdOwner != null; _menu.EjectIDButton.Visible = msg.PDAOwnerInfo.IdOwner != null;
_menu.EjectPenButton.Visible = msg.HasPen;
if (msg.Account != null) if (msg.Account != null)
{ {
_menu.CurrentLoggedInAccount = msg.Account; _menu.CurrentLoggedInAccount = msg.Account;
@@ -220,6 +226,7 @@ namespace Content.Client.GameObjects.Components.PDA
public Button FlashLightToggleButton { get; } public Button FlashLightToggleButton { get; }
public Button EjectIDButton { get; } public Button EjectIDButton { get; }
public Button EjectPenButton { get; }
public TabContainer MasterTabContainer; public TabContainer MasterTabContainer;
@@ -288,13 +295,20 @@ namespace Content.Client.GameObjects.Components.PDA
SizeFlagsHorizontal = SizeFlags.ShrinkCenter, SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
SizeFlagsVertical = SizeFlags.ShrinkCenter SizeFlagsVertical = SizeFlags.ShrinkCenter
}; };
EjectPenButton = new Button
{
Text = Loc.GetString("Eject Pen"),
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
SizeFlagsVertical = SizeFlags.ShrinkCenter
};
var innerHBoxContainer = new HBoxContainer var innerHBoxContainer = new HBoxContainer
{ {
Children = Children =
{ {
IDInfoLabel, IDInfoLabel,
EjectIDButton EjectIDButton,
EjectPenButton
} }
}; };

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Client.UserInterface.Stylesheets;
using Content.Shared.AI; using Content.Shared.AI;
using Robust.Client.Interfaces.Graphics.ClientEye; using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.UserInterface; using Robust.Client.Interfaces.UserInterface;
@@ -178,7 +179,7 @@ namespace Content.Client.GameObjects.EntitySystems.AI
var panel = new PanelContainer var panel = new PanelContainer
{ {
StyleClasses = {"tooltipBox"}, StyleClasses = { StyleNano.StyleClassTooltipPanel },
Children = {vBox}, Children = {vBox},
MouseFilter = Control.MouseFilterMode.Ignore, MouseFilter = Control.MouseFilterMode.Ignore,
ModulateSelfOverride = Color.White.WithAlpha(0.75f), ModulateSelfOverride = Color.White.WithAlpha(0.75f),

View File

@@ -5,7 +5,7 @@ using Robust.Shared.IoC;
namespace Content.Client.GameObjects.EntitySystems namespace Content.Client.GameObjects.EntitySystems
{ {
public class StatusEffectsSystem : EntitySystem public class AlertsSystem : EntitySystem
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -16,9 +16,9 @@ namespace Content.Client.GameObjects.EntitySystems
if (!_gameTiming.IsFirstTimePredicted) if (!_gameTiming.IsFirstTimePredicted)
return; return;
foreach (var clientStatusEffectsComponent in EntityManager.ComponentManager.EntityQuery<ClientStatusEffectsComponent>()) foreach (var clientAlertsComponent in EntityManager.ComponentManager.EntityQuery<ClientAlertsComponent>())
{ {
clientStatusEffectsComponent.FrameUpdate(frameTime); clientAlertsComponent.FrameUpdate(frameTime);
} }
} }
} }

View File

@@ -56,23 +56,20 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter
/// Is the cancellation bar red? /// Is the cancellation bar red?
/// </summary> /// </summary>
private bool _flash = true; private bool _flash = true;
/// <summary> /// <summary>
/// Last time we swapped the flash. /// Last time we swapped the flash.
/// </summary> /// </summary>
private TimeSpan _lastFlash; private TimeSpan _lastFlash;
/// <summary> /// <summary>
/// How long each cancellation bar flash lasts in seconds. /// How long each cancellation bar flash lasts in seconds.
/// </summary> /// </summary>
private const float FlashTime = 0.125f; private const float FlashTime = 0.125f;
private const int XPixelDiff = 20 * DoAfterBarScale; private const int XPixelDiff = 20 * DoAfterBarScale;
public const byte DoAfterBarScale = 2; public const byte DoAfterBarScale = 2;
private static readonly Color StartColor = new Color(0.8f, 0.0f, 0.2f); // red
private static readonly Color EndColor = new Color(0.92f, 0.77f, 0.34f); // yellow
private static readonly Color CompletedColor = new Color(0.0f, 0.8f, 0.27f); // green
public DoAfterBar() public DoAfterBar()
{ {
@@ -108,28 +105,25 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter
} }
color = new Color(1.0f, 0.0f, 0.0f, _flash ? 1.0f : 0.0f); color = new Color(1.0f, 0.0f, 0.0f, _flash ? 1.0f : 0.0f);
} }
else if (Ratio >= 1.0f) else if (Ratio >= 1.0f)
{ {
color = CompletedColor; color = new Color(0f, 1f, 0f);
} }
else else
{ {
// lerp // lerp
color = new Color( var hue = (5f / 18f) * Ratio;
StartColor.R + (EndColor.R - StartColor.R) * Ratio, color = Color.FromHsv((hue, 1f, 0.75f, 1f));
StartColor.G + (EndColor.G - StartColor.G) * Ratio,
StartColor.B + (EndColor.B - StartColor.B) * Ratio,
StartColor.A);
} }
handle.UseShader(_shader); handle.UseShader(_shader);
// If you want to make this less hard-coded be my guest // If you want to make this less hard-coded be my guest
var leftOffset = 2 * DoAfterBarScale; var leftOffset = 2 * DoAfterBarScale;
var box = new UIBox2i( var box = new UIBox2i(
leftOffset, leftOffset,
-2 + 2 * DoAfterBarScale, -2 + 2 * DoAfterBarScale,
leftOffset + (int) (XPixelDiff * Ratio), leftOffset + (int) (XPixelDiff * Ratio),
-2); -2);
handle.DrawRect(box, color); handle.DrawRect(box, color);
} }

View File

@@ -58,7 +58,6 @@
"Drink", "Drink",
"Food", "Food",
"FoodContainer", "FoodContainer",
"Stomach",
"Rotatable", "Rotatable",
"MagicMirror", "MagicMirror",
"FloorTile", "FloorTile",
@@ -180,10 +179,8 @@
"BreakableConstruction", "BreakableConstruction",
"GasCanister", "GasCanister",
"GasCanisterPort", "GasCanisterPort",
"Lung",
"Cleanable", "Cleanable",
"Configuration", "Configuration",
"Brain",
"PlantHolder", "PlantHolder",
"SeedExtractor", "SeedExtractor",
"Produce", "Produce",

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Client.Interfaces.Parallax; using Content.Client.Interfaces.Parallax;
using Content.Shared;
using Nett; using Nett;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement; using Robust.Client.Interfaces.ResourceManagement;
@@ -34,12 +35,12 @@ namespace Content.Client.Parallax
public async void LoadParallax() public async void LoadParallax()
{ {
if (!_configurationManager.GetCVar<bool>("parallax.enabled")) if (!_configurationManager.GetCVar(CCVars.ParallaxEnabled))
{ {
return; return;
} }
var debugParallax = _configurationManager.GetCVar<bool>("parallax.debug"); var debugParallax = _configurationManager.GetCVar(CCVars.ParallaxDebug);
string contents; string contents;
TomlTable table; TomlTable table;
// Load normal config into memory // Load normal config into memory

View File

@@ -32,6 +32,7 @@ namespace Content.Client.Sandbox
public Button ToggleSubfloorButton; public Button ToggleSubfloorButton;
public Button ShowMarkersButton; //Shows spawn points public Button ShowMarkersButton; //Shows spawn points
public Button ShowBbButton; //Shows bounding boxes public Button ShowBbButton; //Shows bounding boxes
public Button MachineLinkingButton; // Enables/disables machine linking mode.
public SandboxWindow() public SandboxWindow()
{ {
@@ -77,6 +78,9 @@ namespace Content.Client.Sandbox
ShowBbButton = new Button { Text = Loc.GetString("Show Bb"), ToggleMode = true }; ShowBbButton = new Button { Text = Loc.GetString("Show Bb"), ToggleMode = true };
vBox.AddChild(ShowBbButton); vBox.AddChild(ShowBbButton);
MachineLinkingButton = new Button { Text = Loc.GetString("Link machines"), ToggleMode = true };
vBox.AddChild(MachineLinkingButton);
} }
} }
@@ -186,6 +190,7 @@ namespace Content.Client.Sandbox
_window.ToggleSubfloorButton.OnPressed += OnToggleSubfloorButtonClicked; _window.ToggleSubfloorButton.OnPressed += OnToggleSubfloorButtonClicked;
_window.ShowMarkersButton.OnPressed += OnShowMarkersButtonClicked; _window.ShowMarkersButton.OnPressed += OnShowMarkersButtonClicked;
_window.ShowBbButton.OnPressed += OnShowBbButtonClicked; _window.ShowBbButton.OnPressed += OnShowBbButtonClicked;
_window.MachineLinkingButton.OnPressed += OnMachineLinkingButtonClicked;
_window.OpenCentered(); _window.OpenCentered();
} }
@@ -241,6 +246,10 @@ namespace Content.Client.Sandbox
{ {
ShowBb(); ShowBb();
} }
private void OnMachineLinkingButtonClicked(BaseButton.ButtonEventArgs args)
{
LinkMachines();
}
private void OnGiveAdminAccessButtonClicked(BaseButton.ButtonEventArgs args) private void OnGiveAdminAccessButtonClicked(BaseButton.ButtonEventArgs args)
{ {
@@ -318,5 +327,10 @@ namespace Content.Client.Sandbox
{ {
_console.ProcessCommand("showbb"); _console.ProcessCommand("showbb");
} }
private void LinkMachines()
{
_console.ProcessCommand("signallink");
}
} }
} }

View File

@@ -3,6 +3,7 @@ using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Content.Client.GameObjects.Components; using Content.Client.GameObjects.Components;
using Content.Client.Utility; using Content.Client.Utility;
using Content.Shared;
using Robust.Client.GameObjects.EntitySystems; using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Interfaces.GameObjects; using Robust.Client.Interfaces.GameObjects;
using Robust.Client.Interfaces.Graphics.ClientEye; using Robust.Client.Interfaces.Graphics.ClientEye;
@@ -71,7 +72,7 @@ namespace Content.Client.State
} }
InteractionOutlineComponent outline; InteractionOutlineComponent outline;
if(!ConfigurationManager.GetCVar<bool>("outline.enabled")) if(!ConfigurationManager.GetCVar(CCVars.OutlineEnabled))
{ {
if(entityToClick != null && entityToClick.TryGetComponent(out outline)) if(entityToClick != null && entityToClick.TryGetComponent(out outline))
{ {

View File

@@ -8,6 +8,7 @@ using Robust.Client.Interfaces.UserInterface;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared;
using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Network; using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC; using Robust.Shared.IoC;
@@ -111,10 +112,10 @@ namespace Content.Client.State
return; return;
} }
var configName = _configurationManager.GetCVar<string>("player.name"); var configName = _configurationManager.GetCVar(CVars.PlayerName);
if (_mainMenuControl.UserNameBox.Text != configName) if (_mainMenuControl.UserNameBox.Text != configName)
{ {
_configurationManager.SetCVar("player.name", inputName); _configurationManager.SetCVar(CVars.PlayerName, inputName);
_configurationManager.SaveToFile(); _configurationManager.SaveToFile();
} }
@@ -248,7 +249,7 @@ namespace Content.Client.State
vBox.AddChild(userNameHBox); vBox.AddChild(userNameHBox);
userNameHBox.AddChild(new Label {Text = "Username:"}); userNameHBox.AddChild(new Label {Text = "Username:"});
var currentUserName = _configurationManager.GetCVar<string>("player.name"); var currentUserName = _configurationManager.GetCVar(CVars.PlayerName);
UserNameBox = new LineEdit UserNameBox = new LineEdit
{ {
Text = currentUserName, PlaceHolder = "Username", Text = currentUserName, PlaceHolder = "Username",

View File

@@ -0,0 +1,73 @@
using System;
using Content.Client.UserInterface.Stylesheets;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Maths;
namespace Content.Client.UserInterface
{
/// <summary>
/// The status effects display on the right side of the screen.
/// </summary>
public sealed class AlertsUI : Control
{
public GridContainer Grid { get; }
private readonly IClyde _clyde;
public AlertsUI(IClyde clyde)
{
_clyde = clyde;
var panelContainer = new PanelContainer
{
StyleClasses = {StyleNano.StyleClassTransparentBorderedWindowPanel},
SizeFlagsVertical = SizeFlags.FillExpand,
};
AddChild(panelContainer);
Grid = new GridContainer
{
MaxHeight = CalcMaxHeight(clyde.ScreenSize),
ExpandBackwards = true
};
panelContainer.AddChild(Grid);
clyde.OnWindowResized += ClydeOnOnWindowResized;
LayoutContainer.SetGrowHorizontal(this, LayoutContainer.GrowDirection.Begin);
LayoutContainer.SetAnchorAndMarginPreset(this, LayoutContainer.LayoutPreset.TopRight, margin: 10);
LayoutContainer.SetMarginTop(this, 250);
}
protected override void UIScaleChanged()
{
Grid.MaxHeight = CalcMaxHeight(_clyde.ScreenSize);
base.UIScaleChanged();
}
private void ClydeOnOnWindowResized(WindowResizedEventArgs obj)
{
// TODO: Can rework this once https://github.com/space-wizards/RobustToolbox/issues/1392 is done,
// this is here because there isn't currently a good way to allow the grid to adjust its height based
// on constraints, otherwise we would use anchors to lay it out
Grid.MaxHeight = CalcMaxHeight(obj.NewSize);;
}
private float CalcMaxHeight(Vector2i screenSize)
{
return Math.Max(((screenSize.Y) / UIScale) - 420, 1);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_clyde.OnWindowResized -= ClydeOnOnWindowResized;
}
}
}
}

View File

@@ -149,7 +149,7 @@ namespace Content.Client.UserInterface
PanelOverride = new StyleBoxFlat {BackgroundColor = StyleNano.NanoGold}, PanelOverride = new StyleBoxFlat {BackgroundColor = StyleNano.NanoGold},
CustomMinimumSize = (2, 0) CustomMinimumSize = (2, 0)
}); });
_humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager); _humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager, entityManager);
_humanoidProfileEditor.OnProfileChanged += newProfile => { UpdateUI(); }; _humanoidProfileEditor.OnProfileChanged += newProfile => { UpdateUI(); };
hBox.AddChild(_humanoidProfileEditor); hBox.AddChild(_humanoidProfileEditor);
@@ -158,6 +158,15 @@ namespace Content.Client.UserInterface
preferencesManager.OnServerDataLoaded += UpdateUI; preferencesManager.OnServerDataLoaded += UpdateUI;
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_preferencesManager.OnServerDataLoaded -= UpdateUI;
}
public void Save() => _humanoidProfileEditor.Save(); public void Save() => _humanoidProfileEditor.Save();
private void UpdateUI() private void UpdateUI()
@@ -283,7 +292,9 @@ namespace Content.Client.UserInterface
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
base.Dispose(disposing); base.Dispose(disposing);
if (!disposing) return; if (!disposing)
return;
_previewDummy.Delete(); _previewDummy.Delete();
_previewDummy = null; _previewDummy = null;
} }

View File

@@ -27,29 +27,23 @@ namespace Content.Client.UserInterface
/// Possible values range from 1 to -1, where 1 to 0 is a depleting circle animation and 0 to -1 is a blink animation. /// Possible values range from 1 to -1, where 1 to 0 is a depleting circle animation and 0 to -1 is a blink animation.
/// </summary> /// </summary>
public float Progress { get; set; } public float Progress { get; set; }
private static readonly Color StartColor = new Color(0.8f, 0.0f, 0.2f); // red
private static readonly Color EndColor = new Color(0.92f, 0.77f, 0.34f); // yellow
private static readonly Color CompletedColor = new Color(0.0f, 0.8f, 0.27f); // green
protected override void Draw(DrawingHandleScreen handle) protected override void Draw(DrawingHandleScreen handle)
{ {
Span<float> x = stackalloc float[10]; Span<float> x = new float[10];
Color color; Color color;
var lerp = 1f - MathF.Abs(Progress); // for future bikeshedding purposes var lerp = 1f - MathF.Abs(Progress); // for future bikeshedding purposes
if (Progress >= 0f) if (Progress >= 0f)
{ {
color = new Color( var hue = (5f / 18f) * lerp;
EndColor.R + (StartColor.R - EndColor.R) * Progress, color = Color.FromHsv((hue, 0.75f, 0.75f, 0.50f));
EndColor.G + (StartColor.G - EndColor.G) * Progress,
EndColor.B + (StartColor.B - EndColor.B) * Progress,
EndColor.A);
} }
else else
{ {
var alpha = MathHelper.Clamp(0.5f * lerp, 0f, 0.5f); var alpha = MathHelper.Clamp(0.5f * lerp, 0f, 0.5f);
color = CompletedColor.WithAlpha(alpha); color = new Color(1f, 1f, 1f, alpha);
} }
_shader.SetParameter("progress", Progress); _shader.SetParameter("progress", Progress);

View File

@@ -1,22 +1,25 @@
using System; using Content.Client.GameObjects.Components;
using System.Collections.Generic; using Content.Client.GameObjects.Components.Mobs;
using System.Linq;
using Content.Client.GameObjects.Components;
using Content.Client.Interfaces; using Content.Client.Interfaces;
using Content.Shared;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Client.GameObjects;
using Robust.Client.Graphics.Drawing; using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.Utility; using Robust.Client.Utility;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Content.Client.UserInterface namespace Content.Client.UserInterface
{ {
@@ -43,17 +46,25 @@ namespace Content.Client.UserInterface
private readonly OptionButton _preferenceUnavailableButton; private readonly OptionButton _preferenceUnavailableButton;
private readonly List<AntagPreferenceSelector> _antagPreferences; private readonly List<AntagPreferenceSelector> _antagPreferences;
private readonly IEntity _previewDummy;
private readonly SpriteView _previewSprite;
private readonly SpriteView _previewSpriteSide;
private bool _isDirty; private bool _isDirty;
public int CharacterSlot; public int CharacterSlot;
public HumanoidCharacterProfile Profile; public HumanoidCharacterProfile Profile;
public event Action<HumanoidCharacterProfile> OnProfileChanged; public event Action<HumanoidCharacterProfile> OnProfileChanged;
public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager) public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager, IEntityManager entityManager)
{ {
_random = IoCManager.Resolve<IRobustRandom>(); _random = IoCManager.Resolve<IRobustRandom>();
_preferencesManager = preferencesManager; _preferencesManager = preferencesManager;
var hbox = new HBoxContainer();
AddChild(hbox);
#region Left
var margin = new MarginContainer var margin = new MarginContainer
{ {
MarginTopOverride = 10, MarginTopOverride = 10,
@@ -61,7 +72,7 @@ namespace Content.Client.UserInterface
MarginLeftOverride = 10, MarginLeftOverride = 10,
MarginRightOverride = 10 MarginRightOverride = 10
}; };
AddChild(margin); hbox.AddChild(margin);
var vBox = new VBoxContainer(); var vBox = new VBoxContainer();
margin.AddChild(vBox); margin.AddChild(vBox);
@@ -98,7 +109,7 @@ namespace Content.Client.UserInterface
{ {
SizeFlagsVertical = SizeFlags.FillExpand SizeFlagsVertical = SizeFlags.FillExpand
}; };
var nameLabel = new Label {Text = Loc.GetString("Name:")}; var nameLabel = new Label { Text = Loc.GetString("Name:") };
_nameEdit = new LineEdit _nameEdit = new LineEdit
{ {
CustomMinimumSize = (270, 0), CustomMinimumSize = (270, 0),
@@ -119,7 +130,7 @@ namespace Content.Client.UserInterface
#endregion Name #endregion Name
var tabContainer = new TabContainer {SizeFlagsVertical = SizeFlags.FillExpand}; var tabContainer = new TabContainer { SizeFlagsVertical = SizeFlags.FillExpand };
vBox.AddChild(tabContainer); vBox.AddChild(tabContainer);
#region Appearance #region Appearance
@@ -141,7 +152,7 @@ namespace Content.Client.UserInterface
{ {
var panel = HighlightedContainer(); var panel = HighlightedContainer();
var hBox = new HBoxContainer(); var hBox = new HBoxContainer();
var sexLabel = new Label {Text = Loc.GetString("Sex:")}; var sexLabel = new Label { Text = Loc.GetString("Sex:") };
var sexButtonGroup = new ButtonGroup(); var sexButtonGroup = new ButtonGroup();
@@ -171,8 +182,8 @@ namespace Content.Client.UserInterface
{ {
var panel = HighlightedContainer(); var panel = HighlightedContainer();
var hBox = new HBoxContainer(); var hBox = new HBoxContainer();
var ageLabel = new Label {Text = Loc.GetString("Age:")}; var ageLabel = new Label { Text = Loc.GetString("Age:") };
_ageEdit = new LineEdit {CustomMinimumSize = (40, 0)}; _ageEdit = new LineEdit { CustomMinimumSize = (40, 0) };
_ageEdit.OnTextChanged += args => _ageEdit.OnTextChanged += args =>
{ {
if (!int.TryParse(args.Text, out var newAge)) if (!int.TryParse(args.Text, out var newAge))
@@ -348,7 +359,7 @@ namespace Content.Client.UserInterface
foreach (var antag in prototypeManager.EnumeratePrototypes<AntagPrototype>().OrderBy(a => a.Name)) foreach (var antag in prototypeManager.EnumeratePrototypes<AntagPrototype>().OrderBy(a => a.Name))
{ {
if(!antag.SetPreference) if (!antag.SetPreference)
{ {
continue; continue;
} }
@@ -410,6 +421,71 @@ namespace Content.Client.UserInterface
#endregion Save #endregion Save
#endregion
#region Right
margin = new MarginContainer
{
MarginTopOverride = 10,
MarginBottomOverride = 10,
MarginLeftOverride = 10,
MarginRightOverride = 10
};
hbox.AddChild(margin);
vBox = new VBoxContainer()
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
};
hbox.AddChild(vBox);
#region Preview
_previewDummy = entityManager.SpawnEntity("HumanMob_Dummy", MapCoordinates.Nullspace);
var sprite = _previewDummy.GetComponent<SpriteComponent>();
// Front
var box = new Control()
{
SizeFlagsHorizontal = SizeFlags.Fill,
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 1f,
};
vBox.AddChild(box);
_previewSprite = new SpriteView
{
Sprite = sprite,
Scale = (6, 6),
OverrideDirection = Direction.South,
SizeFlagsVertical = SizeFlags.ShrinkCenter,
SizeFlagsStretchRatio = 1
};
box.AddChild(_previewSprite);
// Side
box = new Control()
{
SizeFlagsHorizontal = SizeFlags.Fill,
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 1f,
};
vBox.AddChild(box);
_previewSpriteSide = new SpriteView
{
Sprite = sprite,
Scale = (6, 6),
OverrideDirection = Direction.East,
SizeFlagsVertical = SizeFlags.ShrinkCenter,
SizeFlagsStretchRatio = 1
};
box.AddChild(_previewSpriteSide);
#endregion
#endregion
if (preferencesManager.ServerDataLoaded) if (preferencesManager.ServerDataLoaded)
{ {
LoadServerData(); LoadServerData();
@@ -420,6 +496,16 @@ namespace Content.Client.UserInterface
IsDirty = false; IsDirty = false;
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_previewDummy.Delete();
_preferencesManager.OnServerDataLoaded -= LoadServerData;
}
private void LoadServerData() private void LoadServerData()
{ {
Profile = (HumanoidCharacterProfile) _preferencesManager.Preferences.SelectedCharacter; Profile = (HumanoidCharacterProfile) _preferencesManager.Preferences.SelectedCharacter;
@@ -458,6 +544,7 @@ namespace Content.Client.UserInterface
set set
{ {
_isDirty = value; _isDirty = value;
UpdatePreview();
UpdateSaveButton(); UpdateSaveButton();
} }
} }
@@ -503,6 +590,15 @@ namespace Content.Client.UserInterface
_saveButton.Disabled = Profile is null || !IsDirty; _saveButton.Disabled = Profile is null || !IsDirty;
} }
private void UpdatePreview()
{
if (Profile is null)
return;
_previewDummy.GetComponent<HumanoidAppearanceComponent>().UpdateFromProfile(Profile);
LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy, Profile);
}
public void UpdateControls() public void UpdateControls()
{ {
if (Profile is null) return; if (Profile is null) return;
@@ -514,6 +610,8 @@ namespace Content.Client.UserInterface
UpdateJobPriorities(); UpdateJobPriorities();
UpdateAntagPreferences(); UpdateAntagPreferences();
UpdatePreview();
_preferenceUnavailableButton.SelectId((int) Profile.PreferenceUnavailable); _preferenceUnavailableButton.SelectId((int) Profile.PreferenceUnavailable);
} }

View File

@@ -1,7 +1,9 @@
using Robust.Client.Graphics; using Content.Shared;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement; using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared;
using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
@@ -121,7 +123,7 @@ namespace Content.Client.UserInterface
}); });
ApplyButton.OnPressed += OnApplyButtonPressed; ApplyButton.OnPressed += OnApplyButtonPressed;
VSyncCheckBox.Pressed = _cfg.GetCVar<bool>("display.vsync"); VSyncCheckBox.Pressed = _cfg.GetCVar(CVars.DisplayVSync);
FullscreenCheckBox.Pressed = ConfigIsFullscreen; FullscreenCheckBox.Pressed = ConfigIsFullscreen;
LightingPresetOption.SelectId(GetConfigLightingQuality()); LightingPresetOption.SelectId(GetConfigLightingQuality());
_uiScaleOption.SelectId(GetConfigUIScalePreset(ConfigUIScale)); _uiScaleOption.SelectId(GetConfigUIScalePreset(ConfigUIScale));
@@ -137,11 +139,11 @@ namespace Content.Client.UserInterface
private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args) private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
{ {
_cfg.SetCVar("display.vsync", VSyncCheckBox.Pressed); _cfg.SetCVar(CVars.DisplayVSync, VSyncCheckBox.Pressed);
SetConfigLightingQuality(LightingPresetOption.SelectedId); SetConfigLightingQuality(LightingPresetOption.SelectedId);
_cfg.SetCVar("display.windowmode", _cfg.SetCVar(CVars.DisplayWindowMode,
(int) (FullscreenCheckBox.Pressed ? WindowMode.Fullscreen : WindowMode.Windowed)); (int) (FullscreenCheckBox.Pressed ? WindowMode.Fullscreen : WindowMode.Windowed));
_cfg.SetCVar("display.uiScale", UIScaleOptions[_uiScaleOption.SelectedId]); _cfg.SetCVar(CVars.DisplayUIScale, UIScaleOptions[_uiScaleOption.SelectedId]);
_cfg.SaveToFile(); _cfg.SaveToFile();
UpdateApplyButton(); UpdateApplyButton();
} }
@@ -159,7 +161,7 @@ namespace Content.Client.UserInterface
private void UpdateApplyButton() private void UpdateApplyButton()
{ {
var isVSyncSame = VSyncCheckBox.Pressed == _cfg.GetCVar<bool>("display.vsync"); var isVSyncSame = VSyncCheckBox.Pressed == _cfg.GetCVar(CVars.DisplayVSync);
var isFullscreenSame = FullscreenCheckBox.Pressed == ConfigIsFullscreen; var isFullscreenSame = FullscreenCheckBox.Pressed == ConfigIsFullscreen;
var isLightingQualitySame = LightingPresetOption.SelectedId == GetConfigLightingQuality(); var isLightingQualitySame = LightingPresetOption.SelectedId == GetConfigLightingQuality();
var isUIScaleSame = MathHelper.CloseTo(UIScaleOptions[_uiScaleOption.SelectedId], ConfigUIScale); var isUIScaleSame = MathHelper.CloseTo(UIScaleOptions[_uiScaleOption.SelectedId], ConfigUIScale);
@@ -167,14 +169,14 @@ namespace Content.Client.UserInterface
} }
private bool ConfigIsFullscreen => private bool ConfigIsFullscreen =>
_cfg.GetCVar<int>("display.windowmode") == (int) WindowMode.Fullscreen; _cfg.GetCVar(CVars.DisplayWindowMode) == (int) WindowMode.Fullscreen;
private float ConfigUIScale => _cfg.GetCVar<float>("display.uiScale"); private float ConfigUIScale => _cfg.GetCVar(CVars.DisplayUIScale);
private int GetConfigLightingQuality() private int GetConfigLightingQuality()
{ {
var val = _cfg.GetCVar<int>("display.lightmapdivider"); var val = _cfg.GetCVar(CVars.DisplayLightMapDivider);
var soft = _cfg.GetCVar<bool>("display.softshadows"); var soft = _cfg.GetCVar(CVars.DisplaySoftShadows);
if (val >= 8) if (val >= 8)
{ {
return 0; return 0;
@@ -198,20 +200,20 @@ namespace Content.Client.UserInterface
switch (value) switch (value)
{ {
case 0: case 0:
_cfg.SetCVar("display.lightmapdivider", 8); _cfg.SetCVar(CVars.DisplayLightMapDivider, 8);
_cfg.SetCVar("display.softshadows", false); _cfg.SetCVar(CVars.DisplaySoftShadows, false);
break; break;
case 1: case 1:
_cfg.SetCVar("display.lightmapdivider", 2); _cfg.SetCVar(CVars.DisplayLightMapDivider, 2);
_cfg.SetCVar("display.softshadows", false); _cfg.SetCVar(CVars.DisplaySoftShadows, false);
break; break;
case 2: case 2:
_cfg.SetCVar("display.lightmapdivider", 2); _cfg.SetCVar(CVars.DisplayLightMapDivider, 2);
_cfg.SetCVar("display.softshadows", true); _cfg.SetCVar(CVars.DisplaySoftShadows, true);
break; break;
case 3: case 3:
_cfg.SetCVar("display.lightmapdivider", 1); _cfg.SetCVar(CVars.DisplayLightMapDivider, 1);
_cfg.SetCVar("display.softshadows", true); _cfg.SetCVar(CVars.DisplaySoftShadows, true);
break; break;
} }
} }

View File

@@ -1,23 +0,0 @@
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.UserInterface
{
/// <summary>
/// The status effects display on the right side of the screen.
/// </summary>
public sealed class StatusEffectsUI : Control
{
public VBoxContainer VBox { get; }
public StatusEffectsUI()
{
VBox = new VBoxContainer();
AddChild(VBox);
LayoutContainer.SetGrowHorizontal(this, LayoutContainer.GrowDirection.Begin);
LayoutContainer.SetAnchorAndMarginPreset(this, LayoutContainer.LayoutPreset.TopRight, margin: 10);
LayoutContainer.SetMarginTop(this, 250);
}
}
}

View File

@@ -13,6 +13,13 @@ namespace Content.Client.UserInterface.Stylesheets
{ {
public sealed class StyleNano : StyleBase public sealed class StyleNano : StyleBase
{ {
public const string StyleClassBorderedWindowPanel = "BorderedWindowPanel";
public const string StyleClassTransparentBorderedWindowPanel = "TransparentBorderedWindowPanel";
public const string StyleClassTooltipPanel = "tooltipBox";
public const string StyleClassTooltipAlertTitle = "tooltipAlertTitle";
public const string StyleClassTooltipAlertDescription = "tooltipAlertDesc";
public const string StyleClassTooltipAlertCooldown = "tooltipAlertCooldown";
public const string StyleClassSliderRed = "Red"; public const string StyleClassSliderRed = "Red";
public const string StyleClassSliderGreen = "Green"; public const string StyleClassSliderGreen = "Green";
public const string StyleClassSliderBlue = "Blue"; public const string StyleClassSliderBlue = "Blue";
@@ -55,6 +62,7 @@ namespace Content.Client.UserInterface.Stylesheets
var notoSansDisplayBold14 = resCache.GetFont("/Fonts/NotoSansDisplay/NotoSansDisplay-Bold.ttf", 14); var notoSansDisplayBold14 = resCache.GetFont("/Fonts/NotoSansDisplay/NotoSansDisplay-Bold.ttf", 14);
var notoSans16 = resCache.GetFont("/Fonts/NotoSans/NotoSans-Regular.ttf", 16); var notoSans16 = resCache.GetFont("/Fonts/NotoSans/NotoSans-Regular.ttf", 16);
var notoSansBold16 = resCache.GetFont("/Fonts/NotoSans/NotoSans-Bold.ttf", 16); var notoSansBold16 = resCache.GetFont("/Fonts/NotoSans/NotoSans-Bold.ttf", 16);
var notoSansBold18 = resCache.GetFont("/Fonts/NotoSans/NotoSans-Bold.ttf", 18);
var notoSansBold20 = resCache.GetFont("/Fonts/NotoSans/NotoSans-Bold.ttf", 20); var notoSansBold20 = resCache.GetFont("/Fonts/NotoSans/NotoSans-Bold.ttf", 20);
var textureCloseButton = resCache.GetTexture("/Textures/Interface/Nano/cross.svg.png"); var textureCloseButton = resCache.GetTexture("/Textures/Interface/Nano/cross.svg.png");
var windowHeaderTex = resCache.GetTexture("/Textures/Interface/Nano/window_header.png"); var windowHeaderTex = resCache.GetTexture("/Textures/Interface/Nano/window_header.png");
@@ -73,6 +81,20 @@ namespace Content.Client.UserInterface.Stylesheets
windowBackground.SetPatchMargin(StyleBox.Margin.Horizontal | StyleBox.Margin.Bottom, 2); windowBackground.SetPatchMargin(StyleBox.Margin.Horizontal | StyleBox.Margin.Bottom, 2);
windowBackground.SetExpandMargin(StyleBox.Margin.Horizontal | StyleBox.Margin.Bottom, 2); windowBackground.SetExpandMargin(StyleBox.Margin.Horizontal | StyleBox.Margin.Bottom, 2);
var borderedWindowBackgroundTex = resCache.GetTexture("/Textures/Interface/Nano/window_background_bordered.png");
var borderedWindowBackground = new StyleBoxTexture
{
Texture = borderedWindowBackgroundTex,
};
borderedWindowBackground.SetPatchMargin(StyleBox.Margin.All, 2);
var borderedTransparentWindowBackgroundTex = resCache.GetTexture("/Textures/Interface/Nano/transparent_window_background_bordered.png");
var borderedTransparentWindowBackground = new StyleBoxTexture
{
Texture = borderedTransparentWindowBackgroundTex,
};
borderedTransparentWindowBackground.SetPatchMargin(StyleBox.Margin.All, 2);
var textureInvertedTriangle = resCache.GetTexture("/Textures/Interface/Nano/inverted_triangle.svg.png"); var textureInvertedTriangle = resCache.GetTexture("/Textures/Interface/Nano/inverted_triangle.svg.png");
var lineEditTex = resCache.GetTexture("/Textures/Interface/Nano/lineedit.png"); var lineEditTex = resCache.GetTexture("/Textures/Interface/Nano/lineedit.png");
@@ -147,7 +169,7 @@ namespace Content.Client.UserInterface.Stylesheets
Texture = tooltipTexture, Texture = tooltipTexture,
}; };
tooltipBox.SetPatchMargin(StyleBox.Margin.All, 2); tooltipBox.SetPatchMargin(StyleBox.Margin.All, 2);
tooltipBox.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5); tooltipBox.SetContentMarginOverride(StyleBox.Margin.Horizontal, 7);
// Placeholder // Placeholder
var placeholderTexture = resCache.GetTexture("/Textures/Interface/Nano/placeholder.png"); var placeholderTexture = resCache.GetTexture("/Textures/Interface/Nano/placeholder.png");
@@ -245,6 +267,19 @@ namespace Content.Client.UserInterface.Stylesheets
{ {
new StyleProperty(PanelContainer.StylePropertyPanel, windowBackground), new StyleProperty(PanelContainer.StylePropertyPanel, windowBackground),
}), }),
// bordered window background
new StyleRule(
new SelectorElement(null, new[] {StyleClassBorderedWindowPanel}, null, null),
new[]
{
new StyleProperty(PanelContainer.StylePropertyPanel, borderedWindowBackground),
}),
new StyleRule(
new SelectorElement(null, new[] {StyleClassTransparentBorderedWindowPanel}, null, null),
new[]
{
new StyleProperty(PanelContainer.StylePropertyPanel, borderedTransparentWindowBackground),
}),
// Window header. // Window header.
new StyleRule( new StyleRule(
new SelectorElement(typeof(PanelContainer), new[] {SS14Window.StyleClassWindowHeader}, null, null), new SelectorElement(typeof(PanelContainer), new[] {SS14Window.StyleClassWindowHeader}, null, null),
@@ -464,7 +499,7 @@ namespace Content.Client.UserInterface.Stylesheets
new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox) new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox)
}), }),
new StyleRule(new SelectorElement(typeof(PanelContainer), new[] {"tooltipBox"}, null, null), new[] new StyleRule(new SelectorElement(typeof(PanelContainer), new [] { StyleClassTooltipPanel }, null, null), new[]
{ {
new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox) new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox)
}), }),
@@ -482,6 +517,20 @@ namespace Content.Client.UserInterface.Stylesheets
new StyleProperty("font", notoSansItalic12), new StyleProperty("font", notoSansItalic12),
}), }),
// alert tooltip
new StyleRule(new SelectorElement(typeof(RichTextLabel), new[] {StyleClassTooltipAlertTitle}, null, null), new[]
{
new StyleProperty("font", notoSansBold18)
}),
new StyleRule(new SelectorElement(typeof(RichTextLabel), new[] {StyleClassTooltipAlertDescription}, null, null), new[]
{
new StyleProperty("font", notoSans16)
}),
new StyleRule(new SelectorElement(typeof(RichTextLabel), new[] {StyleClassTooltipAlertCooldown}, null, null), new[]
{
new StyleProperty("font", notoSans16)
}),
// Entity tooltip // Entity tooltip
new StyleRule( new StyleRule(
new SelectorElement(typeof(PanelContainer), new[] {ExamineSystem.StyleClassEntityTooltip}, null, new SelectorElement(typeof(PanelContainer), new[] {ExamineSystem.StyleClassEntityTooltip}, null,

View File

@@ -20,7 +20,7 @@ using Robust.Shared.Maths;
namespace Content.IntegrationTests.Tests.Body namespace Content.IntegrationTests.Tests.Body
{ {
[TestFixture] [TestFixture]
[TestOf(typeof(LungBehaviorComponent))] [TestOf(typeof(LungBehavior))]
public class LungTest : ContentIntegrationTest public class LungTest : ContentIntegrationTest
{ {
[Test] [Test]
@@ -39,7 +39,7 @@ namespace Content.IntegrationTests.Tests.Body
var human = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace); var human = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace);
Assert.That(human.TryGetComponent(out IBody body)); Assert.That(human.TryGetComponent(out IBody body));
Assert.That(body.TryGetMechanismBehaviors(out List<LungBehaviorComponent> lungs)); Assert.That(body.TryGetMechanismBehaviors(out List<LungBehavior> lungs));
Assert.That(lungs.Count, Is.EqualTo(1)); Assert.That(lungs.Count, Is.EqualTo(1));
Assert.That(human.TryGetComponent(out BloodstreamComponent bloodstream)); Assert.That(human.TryGetComponent(out BloodstreamComponent bloodstream));
@@ -141,7 +141,7 @@ namespace Content.IntegrationTests.Tests.Body
human = entityManager.SpawnEntity("HumanMob_Content", coordinates); human = entityManager.SpawnEntity("HumanMob_Content", coordinates);
Assert.True(human.TryGetComponent(out IBody body)); Assert.True(human.TryGetComponent(out IBody body));
Assert.True(body.HasMechanismBehavior<LungBehaviorComponent>()); Assert.True(body.HasMechanismBehavior<LungBehavior>());
Assert.True(human.TryGetComponent(out metabolism)); Assert.True(human.TryGetComponent(out metabolism));
Assert.False(metabolism.Suffocating); Assert.False(metabolism.Suffocating);
}); });

View File

@@ -1,12 +1,12 @@
#nullable enable #nullable enable
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Body.Behavior;
using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body;
using Content.Shared.GameObjects.Components.Body.Behavior; using Content.Shared.GameObjects.Components.Body.Behavior;
using Content.Shared.GameObjects.Components.Body.Mechanism; using Content.Shared.GameObjects.Components.Body.Mechanism;
using Content.Shared.GameObjects.Components.Body.Part; using Content.Shared.GameObjects.Components.Body.Part;
using NUnit.Framework; using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC; using Robust.Shared.IoC;
@@ -18,14 +18,11 @@ namespace Content.IntegrationTests.Tests.Body
[TestOf(typeof(SharedBodyComponent))] [TestOf(typeof(SharedBodyComponent))]
[TestOf(typeof(SharedBodyPartComponent))] [TestOf(typeof(SharedBodyPartComponent))]
[TestOf(typeof(SharedMechanismComponent))] [TestOf(typeof(SharedMechanismComponent))]
[TestOf(typeof(MechanismBehaviorComponent))] [TestOf(typeof(MechanismBehavior))]
public class MechanismBehaviorEventsTest : ContentIntegrationTest public class MechanismBehaviorEventsTest : ContentIntegrationTest
{ {
[RegisterComponent] private class TestMechanismBehavior : MechanismBehavior
private class TestBehaviorComponent : MechanismBehaviorComponent
{ {
public override string Name => nameof(MechanismBehaviorEventsTest) + "TestBehavior";
public bool WasAddedToBody; public bool WasAddedToBody;
public bool WasAddedToPart; public bool WasAddedToPart;
public bool WasAddedToPartInBody; public bool WasAddedToPartInBody;
@@ -33,8 +30,6 @@ namespace Content.IntegrationTests.Tests.Body
public bool WasRemovedFromPart; public bool WasRemovedFromPart;
public bool WasRemovedFromPartInBody; public bool WasRemovedFromPartInBody;
public override void Update(float frameTime) { }
public bool NoAdded() public bool NoAdded()
{ {
return !WasAddedToBody && !WasAddedToPart && !WasAddedToPartInBody; return !WasAddedToBody && !WasAddedToPart && !WasAddedToPartInBody;
@@ -111,13 +106,7 @@ namespace Content.IntegrationTests.Tests.Body
[Test] [Test]
public async Task EventsTest() public async Task EventsTest()
{ {
var server = StartServerDummyTicker(new ServerContentIntegrationOption var server = StartServerDummyTicker();
{
ContentBeforeIoC = () =>
{
IoCManager.Resolve<IComponentFactory>().Register<TestBehaviorComponent>();
}
});
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
@@ -141,68 +130,68 @@ namespace Content.IntegrationTests.Tests.Body
var mechanism = centerPart!.Mechanisms.First(); var mechanism = centerPart!.Mechanisms.First();
Assert.NotNull(mechanism); Assert.NotNull(mechanism);
var component = mechanism.Owner.AddComponent<TestBehaviorComponent>(); mechanism.EnsureBehavior<TestMechanismBehavior>(out var behavior);
Assert.False(component.WasAddedToBody); Assert.False(behavior.WasAddedToBody);
Assert.False(component.WasAddedToPart); Assert.False(behavior.WasAddedToPart);
Assert.That(component.WasAddedToPartInBody); Assert.That(behavior.WasAddedToPartInBody);
Assert.That(component.NoRemoved); Assert.That(behavior.NoRemoved);
component.ResetAll(); behavior.ResetAll();
Assert.That(component.NoAdded); Assert.That(behavior.NoAdded);
Assert.That(component.NoRemoved); Assert.That(behavior.NoRemoved);
centerPart.RemoveMechanism(mechanism); centerPart.RemoveMechanism(mechanism);
Assert.That(component.NoAdded); Assert.That(behavior.NoAdded);
Assert.False(component.WasRemovedFromBody); Assert.False(behavior.WasRemovedFromBody);
Assert.False(component.WasRemovedFromPart); Assert.False(behavior.WasRemovedFromPart);
Assert.That(component.WasRemovedFromPartInBody); Assert.That(behavior.WasRemovedFromPartInBody);
component.ResetAll(); behavior.ResetAll();
centerPart.TryAddMechanism(mechanism, true); centerPart.TryAddMechanism(mechanism, true);
Assert.False(component.WasAddedToBody); Assert.False(behavior.WasAddedToBody);
Assert.False(component.WasAddedToPart); Assert.False(behavior.WasAddedToPart);
Assert.That(component.WasAddedToPartInBody); Assert.That(behavior.WasAddedToPartInBody);
Assert.That(component.NoRemoved()); Assert.That(behavior.NoRemoved());
component.ResetAll(); behavior.ResetAll();
body.RemovePart(centerPart); body.RemovePart(centerPart);
Assert.That(component.NoAdded); Assert.That(behavior.NoAdded);
Assert.That(component.WasRemovedFromBody); Assert.That(behavior.WasRemovedFromBody);
Assert.False(component.WasRemovedFromPart); Assert.False(behavior.WasRemovedFromPart);
Assert.False(component.WasRemovedFromPartInBody); Assert.False(behavior.WasRemovedFromPartInBody);
component.ResetAll(); behavior.ResetAll();
centerPart.RemoveMechanism(mechanism); centerPart.RemoveMechanism(mechanism);
Assert.That(component.NoAdded); Assert.That(behavior.NoAdded);
Assert.False(component.WasRemovedFromBody); Assert.False(behavior.WasRemovedFromBody);
Assert.That(component.WasRemovedFromPart); Assert.That(behavior.WasRemovedFromPart);
Assert.False(component.WasRemovedFromPartInBody); Assert.False(behavior.WasRemovedFromPartInBody);
component.ResetAll(); behavior.ResetAll();
centerPart.TryAddMechanism(mechanism, true); centerPart.TryAddMechanism(mechanism, true);
Assert.False(component.WasAddedToBody); Assert.False(behavior.WasAddedToBody);
Assert.That(component.WasAddedToPart); Assert.That(behavior.WasAddedToPart);
Assert.False(component.WasAddedToPartInBody); Assert.False(behavior.WasAddedToPartInBody);
Assert.That(component.NoRemoved); Assert.That(behavior.NoRemoved);
component.ResetAll(); behavior.ResetAll();
body.TryAddPart(centerSlot!, centerPart, true); body.TryAddPart(centerSlot!, centerPart, true);
Assert.That(component.WasAddedToBody); Assert.That(behavior.WasAddedToBody);
Assert.False(component.WasAddedToPart); Assert.False(behavior.WasAddedToPart);
Assert.False(component.WasAddedToPartInBody); Assert.False(behavior.WasAddedToPartInBody);
Assert.That(component.NoRemoved); Assert.That(behavior.NoRemoved);
}); });
} }
} }

View File

@@ -302,7 +302,7 @@ namespace Content.IntegrationTests.Tests.Buckle
human.Transform.LocalPosition += (100, 0); human.Transform.LocalPosition += (100, 0);
}); });
await WaitUntil(server, () => !buckle.Buckled, maxTicks: 10); await WaitUntil(server, () => !buckle.Buckled, 10);
Assert.False(buckle.Buckled); Assert.False(buckle.Buckled);

View File

@@ -1,6 +1,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Interfaces.GameTicking; using Content.Server.Interfaces.GameTicking;
using Content.Shared;
using NUnit.Framework; using NUnit.Framework;
using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
@@ -31,7 +32,7 @@ namespace Content.IntegrationTests.Tests.Commands
server.Assert(() => server.Assert(() =>
{ {
configManager.SetCVar("game.lobbyenabled", lobbyEnabled); configManager.SetCVar(CCVars.GameLobbyEnabled, lobbyEnabled);
Assert.That(gameTicker.RunLevel, Is.EqualTo(GameRunLevel.InRound)); Assert.That(gameTicker.RunLevel, Is.EqualTo(GameRunLevel.InRound));

View File

@@ -0,0 +1,110 @@
using System.Linq;
using System.Threading.Tasks;
using Content.Client.GameObjects.Components.Mobs;
using Content.Client.UserInterface;
using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.Alert;
using NUnit.Framework;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
{
[TestFixture]
[TestOf(typeof(ClientAlertsComponent))]
[TestOf(typeof(ServerAlertsComponent))]
public class AlertsComponentTests : ContentIntegrationTest
{
[Test]
public async Task AlertsTest()
{
var (client, server) = await StartConnectedServerClientPair();
await server.WaitIdleAsync();
await client.WaitIdleAsync();
var serverPlayerManager = server.ResolveDependency<Robust.Server.Interfaces.Player.IPlayerManager>();
await server.WaitAssertion(() =>
{
var player = serverPlayerManager.GetAllPlayers().Single();
var playerEnt = player.AttachedEntity;
Assert.NotNull(playerEnt);
var alertsComponent = playerEnt.GetComponent<ServerAlertsComponent>();
Assert.NotNull(alertsComponent);
// show 2 alerts
alertsComponent.ShowAlert(AlertType.Debug1);
alertsComponent.ShowAlert(AlertType.Debug2);
});
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
var clientPlayerMgr = client.ResolveDependency<IPlayerManager>();
var clientUIMgr = client.ResolveDependency<IUserInterfaceManager>();
await client.WaitAssertion(() =>
{
var local = clientPlayerMgr.LocalPlayer;
Assert.NotNull(local);
var controlled = local.ControlledEntity;
Assert.NotNull(controlled);
var alertsComponent = controlled.GetComponent<ClientAlertsComponent>();
Assert.NotNull(alertsComponent);
// find the alertsui
var alertsUI =
clientUIMgr.StateRoot.Children.FirstOrDefault(c => c is AlertsUI) as AlertsUI;
Assert.NotNull(alertsUI);
// we should be seeing 3 alerts - our health, and the 2 debug alerts, in a specific order.
Assert.That(alertsUI.Grid.ChildCount, Is.EqualTo(3));
var alertControls = alertsUI.Grid.Children.Select(c => c as AlertControl);
var alertIDs = alertControls.Select(ac => ac.Alert.AlertType).ToArray();
var expectedIDs = new [] {AlertType.HumanHealth, AlertType.Debug1, AlertType.Debug2};
Assert.That(alertIDs, Is.EqualTo(expectedIDs));
});
await server.WaitAssertion(() =>
{
var player = serverPlayerManager.GetAllPlayers().Single();
var playerEnt = player.AttachedEntity;
Assert.NotNull(playerEnt);
var alertsComponent = playerEnt.GetComponent<ServerAlertsComponent>();
Assert.NotNull(alertsComponent);
alertsComponent.ClearAlert(AlertType.Debug1);
});
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
await client.WaitAssertion(() =>
{
var local = clientPlayerMgr.LocalPlayer;
Assert.NotNull(local);
var controlled = local.ControlledEntity;
Assert.NotNull(controlled);
var alertsComponent = controlled.GetComponent<ClientAlertsComponent>();
Assert.NotNull(alertsComponent);
// find the alertsui
var alertsUI =
clientUIMgr.StateRoot.Children.FirstOrDefault(c => c is AlertsUI) as AlertsUI;
Assert.NotNull(alertsUI);
// we should be seeing only 2 alerts now because one was cleared
Assert.That(alertsUI.Grid.ChildCount, Is.EqualTo(2));
var alertControls = alertsUI.Grid.Children.Select(c => c as AlertControl);
var alertIDs = alertControls.Select(ac => ac.Alert.AlertType).ToArray();
var expectedIDs = new [] {AlertType.HumanHealth, AlertType.Debug2};
Assert.That(alertIDs, Is.EqualTo(expectedIDs));
});
}
}
}

View File

@@ -1,6 +1,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Gravity; using Content.Server.GameObjects.Components.Gravity;
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Gravity; using Content.Shared.GameObjects.Components.Gravity;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
@@ -32,7 +33,7 @@ namespace Content.IntegrationTests.Tests.Gravity
var tileDefinitionManager = server.ResolveDependency<ITileDefinitionManager>(); var tileDefinitionManager = server.ResolveDependency<ITileDefinitionManager>();
IEntity human = null; IEntity human = null;
SharedStatusEffectsComponent statusEffects = null; SharedAlertsComponent alerts = null;
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
@@ -57,7 +58,7 @@ namespace Content.IntegrationTests.Tests.Gravity
human = entityManager.SpawnEntity("HumanMob_Content", coordinates); human = entityManager.SpawnEntity("HumanMob_Content", coordinates);
Assert.True(human.TryGetComponent(out statusEffects)); Assert.True(human.TryGetComponent(out alerts));
}); });
// Let WeightlessSystem and GravitySystem tick // Let WeightlessSystem and GravitySystem tick
@@ -68,7 +69,7 @@ namespace Content.IntegrationTests.Tests.Gravity
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
// No gravity without a gravity generator // No gravity without a gravity generator
Assert.True(statusEffects.Statuses.ContainsKey(StatusEffect.Weightless)); Assert.True(alerts.IsShowingAlert(AlertType.Weightless));
gravityGenerator = human.EnsureComponent<GravityGeneratorComponent>(); gravityGenerator = human.EnsureComponent<GravityGeneratorComponent>();
}); });
@@ -78,7 +79,7 @@ namespace Content.IntegrationTests.Tests.Gravity
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
Assert.False(statusEffects.Statuses.ContainsKey(StatusEffect.Weightless)); Assert.False(alerts.IsShowingAlert(AlertType.Weightless));
// Disable the gravity generator // Disable the gravity generator
var args = new BreakageEventArgs {Owner = human}; var args = new BreakageEventArgs {Owner = human};
@@ -89,7 +90,7 @@ namespace Content.IntegrationTests.Tests.Gravity
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
Assert.False(statusEffects.Statuses.ContainsKey(StatusEffect.Weightless)); Assert.False(alerts.IsShowingAlert(AlertType.Weightless));
}); });
} }
} }

View File

@@ -38,9 +38,7 @@ namespace Content.IntegrationTests.Tests.Lobby
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
var lobbyCvar = CCVars.GameLobbyEnabled; serverConfig.SetCVar(CCVars.GameLobbyEnabled, true);
serverConfig.SetCVar(lobbyCvar.Name, true);
serverTicker.RestartRound(); serverTicker.RestartRound();
}); });

View File

@@ -0,0 +1,66 @@
using System;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Network;
namespace Content.Server.Commands
{
/// <summary>
/// Utilities for writing commands
/// </summary>
public static class CommandUtils
{
/// <summary>
/// Gets the player session for the player with the indicated id,
/// sending a failure to the performer if unable to.
/// </summary>
public static bool TryGetSessionByUsernameOrId(IConsoleShell shell,
string usernameOrId, IPlayerSession performer, out IPlayerSession session)
{
var plyMgr = IoCManager.Resolve<IPlayerManager>();
if (plyMgr.TryGetSessionByUsername(usernameOrId, out session)) return true;
if (Guid.TryParse(usernameOrId, out var targetGuid))
{
if (plyMgr.TryGetSessionById(new NetUserId(targetGuid), out session)) return true;
shell.SendText(performer, "Unable to find user with that name/id.");
return false;
}
shell.SendText(performer, "Unable to find user with that name/id.");
return false;
}
/// <summary>
/// Gets the attached entity for the player session with the indicated id,
/// sending a failure to the performer if unable to.
/// </summary>
public static bool TryGetAttachedEntityByUsernameOrId(IConsoleShell shell,
string usernameOrId, IPlayerSession performer, out IEntity attachedEntity)
{
attachedEntity = null;
if (!TryGetSessionByUsernameOrId(shell, usernameOrId, performer, out var session)) return false;
if (session.AttachedEntity == null)
{
shell.SendText(performer, "User has no attached entity.");
return false;
}
attachedEntity = session.AttachedEntity;
return true;
}
/// <summary>
/// Checks if attached entity is null, returning false and sending a message
/// to performer if not.
/// </summary>
public static bool ValidateAttachedEntity(IConsoleShell shell, IPlayerSession performer, IEntity attachedEntity)
{
if (attachedEntity != null) return true;
shell.SendText(performer, "User has no attached entity.");
return false;
}
}
}

View File

@@ -0,0 +1,39 @@
#nullable enable
using Content.Server.GameObjects.Components.Nutrition;
using Content.Shared.GameObjects.Components.Nutrition;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
namespace Content.Server.Commands
{
public class Hungry : IClientCommand
{
public string Command => "hungry";
public string Description => "Makes you hungry.";
public string Help => $"{Command}";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
{
if (player == null)
{
shell.SendText(player, "You cannot use this command unless you are a player.");
return;
}
if (player.AttachedEntity == null)
{
shell.SendText(player, "You cannot use this command without an entity.");
return;
}
if (!player.AttachedEntity.TryGetComponent(out HungerComponent? hunger))
{
shell.SendText(player, $"Your entity does not have a {nameof(HungerComponent)} component.");
return;
}
var hungryThreshold = hunger.HungerThresholds[HungerThreshold.Starving];
hunger.CurrentHunger = hungryThreshold;
}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Server.Interfaces; using System;
using Content.Server.Interfaces;
using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using System.Collections.Generic; using System.Collections.Generic;
@@ -11,7 +12,8 @@ namespace Content.Server.GameObjects.EntitySystems.DeviceNetwork
{ {
private const int PACKAGES_PER_TICK = 30; private const int PACKAGES_PER_TICK = 30;
private readonly IRobustRandom _random = IoCManager.Resolve<IRobustRandom>(); [Dependency] private readonly IRobustRandom _random = default!;
private readonly Dictionary<int, List<NetworkDevice>> _devices = new Dictionary<int, List<NetworkDevice>>(); private readonly Dictionary<int, List<NetworkDevice>> _devices = new Dictionary<int, List<NetworkDevice>>();
private readonly Queue<NetworkPackage> _packages = new Queue<NetworkPackage>(); private readonly Queue<NetworkPackage> _packages = new Queue<NetworkPackage>();
@@ -40,11 +42,9 @@ namespace Content.Server.GameObjects.EntitySystems.DeviceNetwork
public void Update() public void Update()
{ {
var i = PACKAGES_PER_TICK; var count = Math.Min(PACKAGES_PER_TICK, _packages.Count);
while (_packages.Count > 0 && i > 0) for (var i = 0; i < count; i++)
{ {
i--;
var package = _packages.Dequeue(); var package = _packages.Dequeue();
if (package.Broadcast) if (package.Broadcast)
@@ -72,7 +72,7 @@ namespace Content.Server.GameObjects.EntitySystems.DeviceNetwork
Sender = sender, Sender = sender,
Metadata = metadata Metadata = metadata
}; };
_packages.Enqueue(package); _packages.Enqueue(package);
return true; return true;
} }
@@ -132,7 +132,7 @@ namespace Content.Server.GameObjects.EntitySystems.DeviceNetwork
{ {
var devices = DevicesForFrequency(netId, frequency); var devices = DevicesForFrequency(netId, frequency);
var device = devices.Find(device => device.Address == address); var device = devices.Find(dvc => dvc.Address == address);
return device; return device;
} }
@@ -192,7 +192,6 @@ namespace Content.Server.GameObjects.EntitySystems.DeviceNetwork
public IReadOnlyDictionary<string, string> Data { get; set; } public IReadOnlyDictionary<string, string> Data { get; set; }
public Metadata Metadata; public Metadata Metadata;
public string Sender; public string Sender;
} }
} }
} }

View File

@@ -11,14 +11,14 @@ namespace Content.Server.GameObjects.EntitySystems.DeviceNetwork
private readonly int _netId; private readonly int _netId;
[ViewVariables] [ViewVariables]
public bool Open { get; internal set; } public bool Open { get; private set; }
[ViewVariables] [ViewVariables]
public string Address { get; internal set; } public string Address { get; private set; }
[ViewVariables] [ViewVariables]
public int Frequency { get; internal set; } public int Frequency { get; private set; }
[ViewVariables] [ViewVariables]
public bool RecieveAll public bool ReceiveAll
{ {
get => _network.GetDeviceReceiveAll(_netId, Frequency, Address); get => _network.GetDeviceReceiveAll(_netId, Frequency, Address);
set => _network.SetDeviceReceiveAll(_netId, Frequency, Address, value); set => _network.SetDeviceReceiveAll(_netId, Frequency, Address, value);

View File

@@ -11,6 +11,7 @@ using Content.Server.Interfaces.GameTicking;
using Content.Server.Interfaces.PDA; using Content.Server.Interfaces.PDA;
using Content.Server.Sandbox; using Content.Server.Sandbox;
using Content.Shared.Kitchen; using Content.Shared.Kitchen;
using Content.Shared.Alert;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
@@ -78,6 +79,7 @@ namespace Content.Server
_gameTicker.Initialize(); _gameTicker.Initialize();
IoCManager.Resolve<RecipeManager>().Initialize(); IoCManager.Resolve<RecipeManager>().Initialize();
IoCManager.Resolve<AlertManager>().Initialize();
IoCManager.Resolve<BlackboardManager>().Initialize(); IoCManager.Resolve<BlackboardManager>().Initialize();
IoCManager.Resolve<ConsiderationsManager>().Initialize(); IoCManager.Resolve<ConsiderationsManager>().Initialize();
IoCManager.Resolve<IPDAUplinkManager>().Initialize(); IoCManager.Resolve<IPDAUplinkManager>().Initialize();

View File

@@ -6,6 +6,7 @@ using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.EntitySystems.DoAfter; using Content.Server.GameObjects.EntitySystems.DoAfter;
using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.ActionBlocking; using Content.Shared.GameObjects.Components.ActionBlocking;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
@@ -115,7 +116,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
CanStillInteract = _hands.Hands.Count() > CuffedHandCount; CanStillInteract = _hands.Hands.Count() > CuffedHandCount;
OnCuffedStateChanged.Invoke(); OnCuffedStateChanged.Invoke();
UpdateStatusEffect(); UpdateAlert();
UpdateHeldItems(); UpdateHeldItems();
Dirty(); Dirty();
} }
@@ -181,17 +182,17 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
/// <summary> /// <summary>
/// Updates the status effect indicator on the HUD. /// Updates the status effect indicator on the HUD.
/// </summary> /// </summary>
private void UpdateStatusEffect() private void UpdateAlert()
{ {
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status)) if (Owner.TryGetComponent(out ServerAlertsComponent status))
{ {
if (CanStillInteract) if (CanStillInteract)
{ {
status.RemoveStatusEffect(StatusEffect.Cuffed); status.ClearAlert(AlertType.Handcuffed);
} }
else else
{ {
status.ChangeStatusEffectIcon(StatusEffect.Cuffed, "/Textures/Interface/StatusEffects/Handcuffed/Handcuffed.png"); status.ShowAlert(AlertType.Handcuffed);
} }
} }
} }
@@ -282,7 +283,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
CanStillInteract = _hands.Hands.Count() > CuffedHandCount; CanStillInteract = _hands.Hands.Count() > CuffedHandCount;
OnCuffedStateChanged.Invoke(); OnCuffedStateChanged.Invoke();
UpdateStatusEffect(); UpdateAlert();
Dirty(); Dirty();
if (CuffedHandCount == 0) if (CuffedHandCount == 0)

View File

@@ -299,6 +299,7 @@ namespace Content.Server.GameObjects.Components.Arcade
_component = component; _component = component;
_allBlockGamePieces = (BlockGamePieceType[]) Enum.GetValues(typeof(BlockGamePieceType)); _allBlockGamePieces = (BlockGamePieceType[]) Enum.GetValues(typeof(BlockGamePieceType));
_internalNextPiece = GetRandomBlockGamePiece(_component._random); _internalNextPiece = GetRandomBlockGamePiece(_component._random);
InitializeNewBlock();
} }
private void SendHighscoreUpdate() private void SendHighscoreUpdate()
@@ -315,8 +316,6 @@ namespace Content.Server.GameObjects.Components.Arcade
public void StartGame() public void StartGame()
{ {
InitializeNewBlock();
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game)); _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game));
FullUpdate(); FullUpdate();
@@ -569,10 +568,10 @@ namespace Content.Server.GameObjects.Components.Arcade
break; break;
case BlockGamePlayerAction.Pause: case BlockGamePlayerAction.Pause:
_running = false; _running = false;
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Pause)); _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Pause, _started));
break; break;
case BlockGamePlayerAction.Unpause: case BlockGamePlayerAction.Unpause:
if (!_gameOver) if (!_gameOver && _started)
{ {
_running = true; _running = true;
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game)); _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game));
@@ -583,7 +582,7 @@ namespace Content.Server.GameObjects.Components.Arcade
break; break;
case BlockGamePlayerAction.ShowHighscores: case BlockGamePlayerAction.ShowHighscores:
_running = false; _running = false;
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Highscores)); _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Highscores, _started));
break; break;
} }
} }
@@ -654,6 +653,7 @@ namespace Content.Server.GameObjects.Components.Arcade
} }
private bool IsGameOver => _field.Any(block => block.Position.Y == 0); private bool IsGameOver => _field.Any(block => block.Position.Y == 0);
private void InvokeGameover() private void InvokeGameover()
{ {
_running = false; _running = false;

View File

@@ -2,6 +2,7 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
using Content.Shared.Alert;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
@@ -22,7 +23,7 @@ namespace Content.Server.GameObjects.Components.Atmos
public void Update(float airPressure) public void Update(float airPressure)
{ {
if (!Owner.TryGetComponent(out IDamageableComponent damageable)) return; if (!Owner.TryGetComponent(out IDamageableComponent damageable)) return;
Owner.TryGetComponent(out ServerStatusEffectsComponent status); Owner.TryGetComponent(out ServerAlertsComponent status);
var highPressureMultiplier = 1f; var highPressureMultiplier = 1f;
var lowPressureMultiplier = 1f; var lowPressureMultiplier = 1f;
@@ -50,11 +51,11 @@ namespace Content.Server.GameObjects.Components.Atmos
if (pressure <= Atmospherics.HazardLowPressure) if (pressure <= Atmospherics.HazardLowPressure)
{ {
status.ChangeStatusEffect(StatusEffect.Pressure, "/Textures/Interface/StatusEffects/Pressure/lowpressure2.png", null); status.ShowAlert(AlertType.LowPressure, 2);
break; break;
} }
status.ChangeStatusEffect(StatusEffect.Pressure, "/Textures/Interface/StatusEffects/Pressure/lowpressure1.png", null); status.ShowAlert(AlertType.LowPressure, 1);
break; break;
// High pressure. // High pressure.
@@ -72,16 +73,16 @@ namespace Content.Server.GameObjects.Components.Atmos
if (pressure >= Atmospherics.HazardHighPressure) if (pressure >= Atmospherics.HazardHighPressure)
{ {
status.ChangeStatusEffect(StatusEffect.Pressure, "/Textures/Interface/StatusEffects/Pressure/highpressure2.png", null); status.ShowAlert(AlertType.HighPressure, 2);
break; break;
} }
status.ChangeStatusEffect(StatusEffect.Pressure, "/Textures/Interface/StatusEffects/Pressure/highpressure1.png", null); status.ShowAlert(AlertType.HighPressure, 1);
break; break;
// Normal pressure. // Normal pressure.
default: default:
status?.RemoveStatusEffect(StatusEffect.Pressure); status?.ClearAlertCategory(AlertCategory.Pressure);
break; break;
} }

View File

@@ -44,11 +44,6 @@ namespace Content.Server.GameObjects.Components.Atmos
AutoClose = false; AutoClose = false;
Safety = false; Safety = false;
if (Occludes && Owner.TryGetComponent(out OccluderComponent occluder))
{
occluder.Enabled = false;
}
State = DoorState.Open; State = DoorState.Open;
SetAppearance(DoorVisualState.Open); SetAppearance(DoorVisualState.Open);
} }

View File

@@ -4,6 +4,7 @@ using Content.Server.Atmos;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Temperature; using Content.Server.GameObjects.Components.Temperature;
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Damage; using Content.Shared.Damage;
@@ -93,15 +94,15 @@ namespace Content.Server.GameObjects.Components.Atmos
FireStacks = MathF.Min(0, FireStacks + 1); FireStacks = MathF.Min(0, FireStacks + 1);
} }
Owner.TryGetComponent(out ServerStatusEffectsComponent status); Owner.TryGetComponent(out ServerAlertsComponent status);
if (!OnFire) if (!OnFire)
{ {
status?.RemoveStatusEffect(StatusEffect.Fire); status?.ClearAlert(AlertType.Fire);
return; return;
} }
status?.ChangeStatusEffect(StatusEffect.Fire, "/Textures/Interface/StatusEffects/Fire/fire.png", null); status.ShowAlert(AlertType.Fire, onClickAlert: OnClickAlert);
if (FireStacks > 0) if (FireStacks > 0)
{ {
@@ -153,6 +154,14 @@ namespace Content.Server.GameObjects.Components.Atmos
} }
} }
private void OnClickAlert(ClickAlertEventArgs args)
{
if (args.Player.TryGetComponent(out FlammableComponent flammable))
{
flammable.Resist();
}
}
public void CollideWith(IEntity collidedWith) public void CollideWith(IEntity collidedWith)
{ {
if (!collidedWith.TryGetComponent(out FlammableComponent otherFlammable)) if (!collidedWith.TryGetComponent(out FlammableComponent otherFlammable))

View File

@@ -1,22 +1,14 @@
#nullable enable #nullable enable
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.Mobs;
using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body;
using Content.Shared.GameObjects.Components.Body.Behavior;
using Content.Shared.GameObjects.Components.Body.Part; using Content.Shared.GameObjects.Components.Body.Part;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.ComponentDependencies;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Body.Behavior namespace Content.Server.GameObjects.Components.Body.Behavior
{ {
[RegisterComponent] public class BrainBehavior : MechanismBehavior
public class BrainBehaviorComponent : MechanismBehaviorComponent
{ {
public override string Name => "Brain";
protected override void OnAddedToBody(IBody body) protected override void OnAddedToBody(IBody body)
{ {
base.OnAddedToBody(body); base.OnAddedToBody(body);
@@ -66,9 +58,5 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
oldMind.Mind?.TransferTo(newEntity); oldMind.Mind?.TransferTo(newEntity);
} }
public override void Update(float frameTime)
{
}
} }
} }

View File

@@ -1,20 +1,17 @@
using Content.Shared.GameObjects.Components.Body.Behavior; using Content.Shared.GameObjects.Components.Body.Behavior;
using Content.Shared.GameObjects.Components.Body.Networks; using Content.Shared.GameObjects.Components.Body.Networks;
using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.Components.Body.Behavior namespace Content.Server.GameObjects.Components.Body.Behavior
{ {
[RegisterComponent] public class HeartBehavior : MechanismBehavior
[ComponentReference(typeof(SharedHeartBehaviorComponent))]
public class HeartBehaviorComponent : SharedHeartBehaviorComponent
{ {
private float _accumulatedFrameTime; private float _accumulatedFrameTime;
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
// TODO BODY do between pre and metabolism // TODO BODY do between pre and metabolism
if (Mechanism?.Body == null || if (Parent.Body == null ||
!Mechanism.Body.Owner.HasComponent<SharedBloodstreamComponent>()) !Parent.Body.Owner.HasComponent<SharedBloodstreamComponent>())
{ {
return; return;
} }

View File

@@ -8,19 +8,15 @@ using Content.Server.GameObjects.Components.Body.Respiratory;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.GameObjects.Components.Body.Behavior; using Content.Shared.GameObjects.Components.Body.Behavior;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Timing; using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Body.Behavior namespace Content.Server.GameObjects.Components.Body.Behavior
{ {
[RegisterComponent] public class LungBehavior : MechanismBehavior
[ComponentReference(typeof(SharedLungBehaviorComponent))]
public class LungBehaviorComponent : SharedLungBehaviorComponent
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -30,18 +26,24 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
[ViewVariables] public GasMixture Air { get; set; } = default!; [ViewVariables] public GasMixture Air { get; set; } = default!;
[ViewVariables] public override float Temperature => Air.Temperature; [ViewVariables] public float Temperature => Air.Temperature;
[ViewVariables] public override float Volume => Air.Volume; [ViewVariables] public float Volume => Air.Volume;
[ViewVariables] public TimeSpan GaspPopupCooldown { get; private set; } [ViewVariables] public TimeSpan GaspPopupCooldown { get; private set; }
[ViewVariables] public LungStatus Status { get; set; }
[ViewVariables] public float CycleDelay { get; set; }
public override void ExposeData(ObjectSerializer serializer) public override void ExposeData(ObjectSerializer serializer)
{ {
base.ExposeData(serializer); base.ExposeData(serializer);
Air = new GasMixture {Temperature = Atmospherics.NormalBodyTemperature}; Air = new GasMixture {Temperature = Atmospherics.NormalBodyTemperature};
serializer.DataField(this, l => l.CycleDelay, "cycleDelay", 2);
serializer.DataReadWriteFunction( serializer.DataReadWriteFunction(
"volume", "volume",
6, 6,
@@ -61,7 +63,7 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
() => GaspPopupCooldown.TotalSeconds); () => GaspPopupCooldown.TotalSeconds);
} }
public override void Gasp() public void Gasp()
{ {
if (_gameTiming.CurTime >= _lastGaspPopupTime + GaspPopupCooldown) if (_gameTiming.CurTime >= _lastGaspPopupTime + GaspPopupCooldown)
{ {
@@ -148,7 +150,7 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
_accumulatedFrameTime = absoluteTime - delay; _accumulatedFrameTime = absoluteTime - delay;
} }
public override void Inhale(float frameTime) public void Inhale(float frameTime)
{ {
if (Body != null && Body.Owner.TryGetComponent(out InternalsComponent? internals) if (Body != null && Body.Owner.TryGetComponent(out InternalsComponent? internals)
&& internals.BreathToolEntity != null && internals.GasTankEntity != null && internals.BreathToolEntity != null && internals.GasTankEntity != null
@@ -176,7 +178,7 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
ToBloodstream(Air); ToBloodstream(Air);
} }
public override void Exhale(float frameTime) public void Exhale(float frameTime)
{ {
if (!Owner.Transform.Coordinates.TryGetTileAir(out var tileAir)) if (!Owner.Transform.Coordinates.TryGetTileAir(out var tileAir))
{ {
@@ -218,4 +220,11 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
Air.Merge(lungRemoved); Air.Merge(lungRemoved);
} }
} }
public enum LungStatus
{
None = 0,
Inhaling,
Exhaling
}
} }

View File

@@ -1,23 +1,33 @@
#nullable enable #nullable enable
using Content.Shared.GameObjects.Components.Body;
using Content.Shared.GameObjects.Components.Body.Behavior;
using Content.Shared.GameObjects.Components.Body.Mechanism; using Content.Shared.GameObjects.Components.Body.Mechanism;
using Content.Shared.GameObjects.Components.Body.Part; using Content.Shared.GameObjects.Components.Body.Part;
using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Shared.GameObjects.Components.Body.Behavior namespace Content.Server.GameObjects.Components.Body.Behavior
{ {
public abstract class MechanismBehaviorComponent : Component, IMechanismBehavior public abstract class MechanismBehavior : IMechanismBehavior
{ {
public IBody? Body => Part?.Body; public IBody? Body => Part?.Body;
public IBodyPart? Part => Mechanism?.Part; public IBodyPart? Part => Parent.Part;
public IMechanism? Mechanism => Owner.GetComponentOrNull<IMechanism>(); public IMechanism Parent { get; private set; } = default!;
protected override void Startup() public IEntity Owner => Parent.Owner;
public virtual void ExposeData(ObjectSerializer serializer) { }
public virtual void Initialize(IMechanism parent)
{ {
base.Startup(); Parent = parent;
}
public virtual void Startup()
{
if (Part == null) if (Part == null)
{ {
return; return;
@@ -33,8 +43,6 @@ namespace Content.Shared.GameObjects.Components.Body.Behavior
} }
} }
public abstract void Update(float frameTime);
public void AddedToBody(IBody body) public void AddedToBody(IBody body)
{ {
DebugTools.AssertNotNull(Body); DebugTools.AssertNotNull(Body);
@@ -98,5 +106,7 @@ namespace Content.Shared.GameObjects.Components.Body.Behavior
protected virtual void OnRemovedFromPart(IBodyPart old) { } protected virtual void OnRemovedFromPart(IBodyPart old) { }
protected virtual void OnRemovedFromPartInBody(IBody oldBody, IBodyPart oldPart) { } protected virtual void OnRemovedFromPartInBody(IBody oldBody, IBodyPart oldPart) { }
public virtual void Update(float frameTime) { }
} }
} }

View File

@@ -2,33 +2,29 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Content.Shared.GameObjects.Components.Body;
using Content.Shared.GameObjects.Components.Body.Behavior; using Content.Shared.GameObjects.Components.Body.Behavior;
using Content.Shared.GameObjects.Components.Body.Part; using Content.Shared.GameObjects.Components.Body.Part;
namespace Content.Shared.GameObjects.Components.Body.Mechanism namespace Content.Server.GameObjects.Components.Body.Behavior
{ {
public static class MechanismExtensions public static class MechanismExtensions
{ {
public static bool HasMechanismBehavior<T>(this IBody body) public static bool HasMechanismBehavior<T>(this IBody body) where T : IMechanismBehavior
{ {
return body.Parts.Values.Any(p => p.HasMechanismBehavior<T>()); return body.Parts.Values.Any(p => p.HasMechanismBehavior<T>());
} }
public static bool HasMechanismBehavior<T>(this IBodyPart part) public static bool HasMechanismBehavior<T>(this IBodyPart part) where T : IMechanismBehavior
{ {
return part.Mechanisms.Any(m => m.Owner.HasComponent<T>()); return part.Mechanisms.Any(m => m.HasBehavior<T>());
}
public static bool HasMechanismBehavior<T>(this IMechanism mechanism)
{
return mechanism.Owner.HasComponent<T>();
} }
public static IEnumerable<IMechanismBehavior> GetMechanismBehaviors(this IBody body) public static IEnumerable<IMechanismBehavior> GetMechanismBehaviors(this IBody body)
{ {
foreach (var part in body.Parts.Values) foreach (var part in body.Parts.Values)
foreach (var mechanism in part.Mechanisms) foreach (var mechanism in part.Mechanisms)
foreach (var behavior in mechanism.Owner.GetAllComponents<IMechanismBehavior>()) foreach (var behavior in mechanism.Behaviors.Values)
{ {
yield return behavior; yield return behavior;
} }
@@ -52,10 +48,11 @@ namespace Content.Shared.GameObjects.Components.Body.Mechanism
{ {
foreach (var part in body.Parts.Values) foreach (var part in body.Parts.Values)
foreach (var mechanism in part.Mechanisms) foreach (var mechanism in part.Mechanisms)
foreach (var behavior in mechanism.Behaviors.Values)
{ {
if (mechanism.Owner.TryGetComponent(out T? behavior)) if (behavior is T tBehavior)
{ {
yield return behavior; yield return tBehavior;
} }
} }
} }

View File

@@ -1,23 +1,23 @@
#nullable enable #nullable enable
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.Chemistry;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Body.Networks; using Content.Shared.GameObjects.Components.Body.Networks;
using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.Components.Chemistry;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.Components.Body.Behavior namespace Content.Server.GameObjects.Components.Body.Behavior
{ {
/// <summary> /// <summary>
/// Where reagents go when ingested. Tracks ingested reagents over time, and /// Where reagents go when ingested. Tracks ingested reagents over time, and
/// eventually transfers them to <see cref="SharedBloodstreamComponent"/> once digested. /// eventually transfers them to <see cref="SharedBloodstreamComponent"/> once digested.
/// </summary> /// </summary>
public abstract class SharedStomachBehaviorComponent : MechanismBehaviorComponent public class StomachBehavior : MechanismBehavior
{ {
public override string Name => "Stomach";
private float _accumulatedFrameTime; private float _accumulatedFrameTime;
/// <summary> /// <summary>
@@ -45,7 +45,7 @@ namespace Content.Shared.GameObjects.Components.Body.Behavior
_accumulatedFrameTime -= 1; _accumulatedFrameTime -= 1;
if (!Body.Owner.TryGetComponent(out SharedSolutionContainerComponent? solution) || if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solution) ||
!Body.Owner.TryGetComponent(out SharedBloodstreamComponent? bloodstream)) !Body.Owner.TryGetComponent(out SharedBloodstreamComponent? bloodstream))
{ {
return; return;
@@ -58,7 +58,7 @@ namespace Content.Shared.GameObjects.Components.Body.Behavior
foreach (var delta in _reagentDeltas.ToList()) foreach (var delta in _reagentDeltas.ToList())
{ {
//Increment lifetime of reagents //Increment lifetime of reagents
delta.Increment(frameTime); delta.Increment(1);
if (delta.Lifetime > _digestionDelay) if (delta.Lifetime > _digestionDelay)
{ {
solution.TryRemoveReagent(delta.ReagentId, delta.Quantity); solution.TryRemoveReagent(delta.ReagentId, delta.Quantity);
@@ -112,6 +112,18 @@ namespace Content.Shared.GameObjects.Components.Body.Behavior
serializer.DataField(ref _digestionDelay, "digestionDelay", 20); serializer.DataField(ref _digestionDelay, "digestionDelay", 20);
} }
public override void Startup()
{
base.Startup();
if (!Owner.EnsureComponent(out SolutionContainerComponent solution))
{
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition} didn't have a {nameof(SolutionContainerComponent)}");
}
solution.MaxVolume = InitialMaxVolume;
}
public bool CanTransferSolution(Solution solution) public bool CanTransferSolution(Solution solution)
{ {
if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solutionComponent)) if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solutionComponent))

View File

@@ -1,24 +0,0 @@
using Content.Server.GameObjects.Components.Chemistry;
using Content.Shared.GameObjects.Components.Body.Behavior;
using Robust.Shared.GameObjects;
using Robust.Shared.Log;
namespace Content.Server.GameObjects.Components.Body.Behavior
{
[RegisterComponent]
[ComponentReference(typeof(SharedStomachBehaviorComponent))]
public class StomachBehaviorComponent : SharedStomachBehaviorComponent
{
protected override void Startup()
{
base.Startup();
if (!Owner.EnsureComponent(out SolutionContainerComponent solution))
{
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition} didn't have a {nameof(SolutionContainerComponent)}");
}
solution.MaxVolume = InitialMaxVolume;
}
}
}

View File

@@ -7,6 +7,7 @@ using Content.Server.GameObjects.Components.Mobs.State;
using Content.Server.GameObjects.Components.Pulling; using Content.Server.GameObjects.Components.Pulling;
using Content.Server.GameObjects.Components.Strap; using Content.Server.GameObjects.Components.Strap;
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Buckle; using Content.Shared.GameObjects.Components.Buckle;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Strap; using Content.Shared.GameObjects.Components.Strap;
@@ -15,21 +16,16 @@ using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Utility; using Content.Shared.Utility;
using NFluidsynth;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystemMessages;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.ComponentDependencies; using Robust.Shared.GameObjects.ComponentDependencies;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Timing; using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -39,12 +35,10 @@ namespace Content.Server.GameObjects.Components.Buckle
[RegisterComponent] [RegisterComponent]
public class BuckleComponent : SharedBuckleComponent, IInteractHand public class BuckleComponent : SharedBuckleComponent, IInteractHand
{ {
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[ComponentDependency] public readonly AppearanceComponent? AppearanceComponent = null; [ComponentDependency] public readonly AppearanceComponent? AppearanceComponent = null;
[ComponentDependency] private readonly ServerStatusEffectsComponent? _serverStatusEffectsComponent = null; [ComponentDependency] private readonly ServerAlertsComponent? _serverAlertsComponent = null;
[ComponentDependency] private readonly StunnableComponent? _stunnableComponent = null; [ComponentDependency] private readonly StunnableComponent? _stunnableComponent = null;
[ComponentDependency] private readonly MobStateManagerComponent? _mobStateManagerComponent = null; [ComponentDependency] private readonly MobStateManagerComponent? _mobStateManagerComponent = null;
@@ -69,7 +63,10 @@ namespace Content.Server.GameObjects.Components.Buckle
[ViewVariables] [ViewVariables]
private TimeSpan _buckleTime; private TimeSpan _buckleTime;
public Vector2? BuckleOffset { get; private set; } /// <summary>
/// The position offset that is being applied to this entity if buckled.
/// </summary>
public Vector2 BuckleOffset { get; private set; }
private StrapComponent? _buckledTo; private StrapComponent? _buckledTo;
@@ -91,20 +88,6 @@ namespace Content.Server.GameObjects.Components.Buckle
[ViewVariables] [ViewVariables]
public override bool Buckled => BuckledTo != null; public override bool Buckled => BuckledTo != null;
/// <summary>
/// True if the entity was inserted or removed from a container
/// before updating, false otherwise.
/// </summary>
[ViewVariables]
private bool ContainerChanged { get; set; }
/// <summary>
/// True if the entity was forcefully moved while buckled and should
/// unbuckle next update, false otherwise
/// </summary>
[ViewVariables]
private bool Moved { get; set; }
/// <summary> /// <summary>
/// The amount of space that this entity occupies in a /// The amount of space that this entity occupies in a
/// <see cref="StrapComponent"/>. /// <see cref="StrapComponent"/>.
@@ -116,26 +99,38 @@ namespace Content.Server.GameObjects.Components.Buckle
/// Shows or hides the buckled status effect depending on if the /// Shows or hides the buckled status effect depending on if the
/// entity is buckled or not. /// entity is buckled or not.
/// </summary> /// </summary>
private void BuckleStatus() private void UpdateBuckleStatus()
{ {
if (_serverStatusEffectsComponent != null) if (_serverAlertsComponent == null)
{ {
if (Buckled) return;
{ }
_serverStatusEffectsComponent.ChangeStatusEffectIcon(StatusEffect.Buckled, BuckledTo!.BuckledIcon);
} if (Buckled)
else {
{ _serverAlertsComponent.ShowAlert(BuckledTo != null ? BuckledTo.BuckledAlertType : AlertType.Buckled,
_serverStatusEffectsComponent.RemoveStatusEffect(StatusEffect.Buckled); onClickAlert: OnClickAlert);
} }
else
{
_serverAlertsComponent.ClearAlertCategory(AlertCategory.Buckled);
} }
} }
private void OnClickAlert(ClickAlertEventArgs args)
{
if (args.Player.TryGetComponent(out BuckleComponent? buckle))
{
buckle.TryUnbuckle(args.Player);
}
}
/// <summary> /// <summary>
/// Reattaches this entity to the strap, modifying its position and rotation. /// Reattaches this entity to the strap, modifying its position and rotation.
/// </summary> /// </summary>
/// <param name="strap">The strap to reattach to.</param> /// <param name="strap">The strap to reattach to.</param>
private void ReAttach(StrapComponent strap) public void ReAttach(StrapComponent strap)
{ {
var ownTransform = Owner.Transform; var ownTransform = Owner.Transform;
var strapTransform = strap.Owner.Transform; var strapTransform = strap.Owner.Transform;
@@ -161,7 +156,7 @@ namespace Content.Server.GameObjects.Components.Buckle
if (strapTransform.WorldRotation.GetCardinalDir() == Direction.North) if (strapTransform.WorldRotation.GetCardinalDir() == Direction.North)
{ {
BuckleOffset = (0, 0.15f); BuckleOffset = (0, 0.15f);
ownTransform.WorldPosition = strapTransform.WorldPosition + BuckleOffset!.Value; ownTransform.WorldPosition = strapTransform.WorldPosition + BuckleOffset;
} }
else else
{ {
@@ -266,8 +261,7 @@ namespace Content.Server.GameObjects.Components.Buckle
return false; return false;
} }
_entitySystem.GetEntitySystem<AudioSystem>() EntitySystem.Get<AudioSystem>().PlayFromEntity(strap.BuckleSound, Owner);
.PlayFromEntity(strap.BuckleSound, Owner);
if (!strap.TryAdd(this)) if (!strap.TryAdd(this))
{ {
@@ -283,13 +277,10 @@ namespace Content.Server.GameObjects.Components.Buckle
BuckledTo = strap; BuckledTo = strap;
ReAttach(strap); ReAttach(strap);
BuckleStatus(); UpdateBuckleStatus();
SendMessage(new BuckleMessage(Owner, to)); SendMessage(new BuckleMessage(Owner, to));
Owner.EntityManager.EventBus.SubscribeEvent<MoveEvent>(EventSource.Local, this, MoveEvent);
if (Owner.TryGetComponent(out PullableComponent? pullableComponent)) if (Owner.TryGetComponent(out PullableComponent? pullableComponent))
{ {
if (pullableComponent.Puller != null) if (pullableComponent.Puller != null)
@@ -315,12 +306,12 @@ namespace Content.Server.GameObjects.Components.Buckle
/// </returns> /// </returns>
public bool TryUnbuckle(IEntity user, bool force = false) public bool TryUnbuckle(IEntity user, bool force = false)
{ {
if (!Buckled) if (BuckledTo == null)
{ {
return false; return false;
} }
StrapComponent oldBuckledTo = BuckledTo!; var oldBuckledTo = BuckledTo;
if (!force) if (!force)
{ {
@@ -360,21 +351,15 @@ namespace Content.Server.GameObjects.Components.Buckle
EntitySystem.Get<StandingStateSystem>().Standing(Owner); EntitySystem.Get<StandingStateSystem>().Standing(Owner);
} }
if (_mobStateManagerComponent != null) _mobStateManagerComponent?.CurrentMobState.EnterState(Owner);
{
_mobStateManagerComponent.CurrentMobState.EnterState(Owner);
}
BuckleStatus(); UpdateBuckleStatus();
oldBuckledTo.Remove(this); oldBuckledTo.Remove(this);
_entitySystem.GetEntitySystem<AudioSystem>() EntitySystem.Get<AudioSystem>().PlayFromEntity(oldBuckledTo.UnbuckleSound, Owner);
.PlayFromEntity(oldBuckledTo.UnbuckleSound, Owner);
SendMessage(new UnbuckleMessage(Owner, oldBuckledTo.Owner)); SendMessage(new UnbuckleMessage(Owner, oldBuckledTo.Owner));
Owner.EntityManager.EventBus.UnsubscribeEvent<MoveEvent>(EventSource.Local, this);
return true; return true;
} }
@@ -402,90 +387,6 @@ namespace Content.Server.GameObjects.Components.Buckle
return TryBuckle(user, to); return TryBuckle(user, to);
} }
/// <summary>
/// Checks if a buckled entity should be unbuckled from moving
/// too far from its strap.
/// </summary>
/// <param name="moveEvent">The move event of a buckled entity.</param>
private void MoveEvent(MoveEvent moveEvent)
{
if (moveEvent.Sender != Owner)
{
return;
}
if (BuckledTo == null || !BuckleOffset.HasValue)
{
return;
}
var bucklePosition = BuckledTo.Owner.Transform.Coordinates.Offset(BuckleOffset.Value);
if (moveEvent.NewPosition.InRange(_entityManager, bucklePosition, 0.2f))
{
return;
}
Moved = true;
}
/// <summary>
/// Called when the owner is inserted or removed from a container,
/// to synchronize the state of buckling.
/// </summary>
/// <param name="message">The message received</param>
private void InsertIntoContainer(ContainerModifiedMessage message)
{
if (message.Entity != Owner)
{
return;
}
ContainerChanged = true;
}
/// <summary>
/// Synchronizes the state of buckling depending on whether the entity
/// was inserted or removed from a container, and whether or not
/// its current strap (if there is one) has also been put into or removed
/// from the same container as well.
/// </summary>
public void Update()
{
if (BuckledTo == null)
{
return;
}
if (Moved)
{
TryUnbuckle(Owner, true);
Moved = false;
return;
}
if (!ContainerChanged)
{
return;
}
var contained = ContainerHelpers.TryGetContainer(Owner, out var ownContainer);
var strapContained = ContainerHelpers.TryGetContainer(BuckledTo.Owner, out var strapContainer);
if (contained != strapContained || ownContainer != strapContainer)
{
TryUnbuckle(Owner, true);
return;
}
if (!contained && !strapContained)
{
ReAttach(BuckledTo);
}
ContainerChanged = false;
}
public override void ExposeData(ObjectSerializer serializer) public override void ExposeData(ObjectSerializer serializer)
{ {
base.ExposeData(serializer); base.ExposeData(serializer);
@@ -499,32 +400,21 @@ namespace Content.Server.GameObjects.Components.Buckle
_unbuckleDelay = TimeSpan.FromSeconds(seconds); _unbuckleDelay = TimeSpan.FromSeconds(seconds);
} }
public override void Initialize()
{
base.Initialize();
_entityManager.EventBus.SubscribeEvent<EntInsertedIntoContainerMessage>(EventSource.Local, this, InsertIntoContainer);
_entityManager.EventBus.SubscribeEvent<EntRemovedFromContainerMessage>(EventSource.Local, this, InsertIntoContainer);
}
protected override void Startup() protected override void Startup()
{ {
base.Startup(); base.Startup();
BuckleStatus(); UpdateBuckleStatus();
} }
public override void OnRemove() public override void OnRemove()
{ {
base.OnRemove(); base.OnRemove();
_entityManager.EventBus.UnsubscribeEvents(this);
BuckledTo?.Remove(this); BuckledTo?.Remove(this);
TryUnbuckle(Owner, true); TryUnbuckle(Owner, true);
_buckleTime = default; _buckleTime = default;
BuckleStatus(); UpdateBuckleStatus();
} }
public override ComponentState GetComponentState() public override ComponentState GetComponentState()

View File

@@ -84,7 +84,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
var trueTarget = target ?? user; var trueTarget = target ?? user;
if (!trueTarget.TryGetComponent(out IBody body) || if (!trueTarget.TryGetComponent(out IBody body) ||
!body.TryGetMechanismBehaviors<StomachBehaviorComponent>(out var stomachs)) !body.TryGetMechanismBehaviors<StomachBehavior>(out var stomachs))
{ {
return false; return false;
} }

View File

@@ -8,9 +8,7 @@ using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -28,18 +26,24 @@ namespace Content.Server.GameObjects.Components
private Regex _validation; private Regex _validation;
public event Action<Dictionary<string, string>> OnConfigUpdate; public override void OnAdd()
public override void Initialize()
{ {
base.Initialize(); base.OnAdd();
if (UserInterface != null) if (UserInterface != null)
{ {
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage; UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
} }
} }
public override void OnRemove()
{
base.OnRemove();
if (UserInterface != null)
{
UserInterface.OnReceiveMessage -= UserInterfaceOnReceiveMessage;
}
}
public override void ExposeData(ObjectSerializer serializer) public override void ExposeData(ObjectSerializer serializer)
{ {
base.ExposeData(serializer); base.ExposeData(serializer);
@@ -48,7 +52,7 @@ namespace Content.Server.GameObjects.Components
(list) => FillConfiguration(list, _config, ""), (list) => FillConfiguration(list, _config, ""),
() => _config.Keys.ToList()); () => _config.Keys.ToList());
serializer.DataReadFunction("vailidation", "^[a-zA-Z0-9 ]*$", value => _validation = new Regex("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled)); serializer.DataReadFunction("validation", "^[a-zA-Z0-9 ]*$", value => _validation = new Regex("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled));
} }
public string GetConfig(string name) public string GetConfig(string name)
@@ -90,22 +94,19 @@ namespace Content.Server.GameObjects.Components
{ {
var value = msg.Config.GetValueOrDefault(key); var value = msg.Config.GetValueOrDefault(key);
if (_validation != null && !_validation.IsMatch(value) && value != "") if (value == null || _validation != null && !_validation.IsMatch(value) && value != "")
continue; continue;
_config[key] = value; _config[key] = value;
} }
OnConfigUpdate(_config); SendMessage(new ConfigUpdatedComponentMessage(config));
} }
} }
private void UpdateUserInterface() private void UpdateUserInterface()
{ {
if (UserInterface == null) UserInterface?.SetState(new ConfigurationBoundUserInterfaceState(_config));
return;
UserInterface.SetState(new ConfigurationBoundUserInterfaceState(_config));
} }
private static void FillConfiguration<T>(List<string> list, Dictionary<string, T> configuration, T value){ private static void FillConfiguration<T>(List<string> list, Dictionary<string, T> configuration, T value){

View File

@@ -13,6 +13,7 @@ using Content.Server.GameObjects.EntitySystems.DoAfter;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body;
using Content.Shared.GameObjects.Components.Disposal; using Content.Shared.GameObjects.Components.Disposal;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
@@ -603,12 +604,7 @@ namespace Content.Server.GameObjects.Components.Disposal
UserInterface.OnReceiveMessage += OnUiReceiveMessage; UserInterface.OnReceiveMessage += OnUiReceiveMessage;
} }
var network = IoCManager.Resolve<IDeviceNetwork>();
_connection = new WiredNetworkConnection(OnReceiveNetMessage, false, Owner); _connection = new WiredNetworkConnection(OnReceiveNetMessage, false, Owner);
if (Owner.TryGetComponent<ConfigurationComponent>(out var configuration))
configuration.OnConfigUpdate += OnConfigUpdate;
UpdateInterface(); UpdateInterface();
} }
@@ -673,6 +669,9 @@ namespace Content.Server.GameObjects.Components.Disposal
switch (message) switch (message)
{ {
case SharedConfigurationComponent.ConfigUpdatedComponentMessage msg:
OnConfigUpdate(msg.Config);
break;
case RelayMovementEntityMessage msg: case RelayMovementEntityMessage msg:
if (!msg.Entity.TryGetComponent(out HandsComponent? hands) || if (!msg.Entity.TryGetComponent(out HandsComponent? hands) ||
hands.Count == 0 || hands.Count == 0 ||
@@ -745,7 +744,7 @@ namespace Content.Server.GameObjects.Components.Disposal
return false; return false;
} }
// Duplicated code here, not sure how else to get actor inside to make UserInterface happy. // Duplicated code here, not sure how else to get actor inside to make UserInterface happy.
if (IsValidInteraction(eventArgs)) if (IsValidInteraction(eventArgs))
{ {

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.Components.Stack;
using Content.Shared.Audio;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Maps; using Content.Shared.Maps;
using Content.Shared.Utility; using Content.Shared.Utility;
@@ -8,6 +9,7 @@ using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Items namespace Content.Server.GameObjects.Components.Items
@@ -33,23 +35,52 @@ namespace Content.Server.GameObjects.Components.Items
Owner.EnsureComponent<StackComponent>(); Owner.EnsureComponent<StackComponent>();
} }
private bool HasBaseTurf(ContentTileDefinition tileDef, string baseTurf)
{
foreach (var tileBaseTurf in tileDef.BaseTurfs)
{
if (baseTurf == tileBaseTurf)
{
return true;
}
}
return false;
}
private void PlaceAt(IMapGrid mapGrid, EntityCoordinates location, ushort tileId, float offset = 0)
{
mapGrid.SetTile(location.Offset(new Vector2(offset, offset)), new Tile(tileId));
EntitySystem.Get<AudioSystem>().PlayAtCoords("/Audio/Items/genhit.ogg", location, AudioHelpers.WithVariation(0.125f));
}
public void AfterInteract(AfterInteractEventArgs eventArgs) public void AfterInteract(AfterInteractEventArgs eventArgs)
{ {
if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return; if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return;
if (!Owner.TryGetComponent(out StackComponent stack)) return; if (!Owner.TryGetComponent(out StackComponent stack)) return;
var attacked = eventArgs.Target; var location = eventArgs.ClickLocation.AlignWithClosestGridTile();
var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GetGridId(Owner.EntityManager)); var locationMap = location.ToMap(Owner.EntityManager);
var tile = mapGrid.GetTileRef(eventArgs.ClickLocation);
var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId];
if (tileDef.IsSubFloor && attacked == null && stack.Use(1)) var desiredTile = (ContentTileDefinition)_tileDefinitionManager[_outputTile];
if (_mapManager.TryGetGrid(location.GetGridId(Owner.EntityManager), out var mapGrid))
{ {
var desiredTile = _tileDefinitionManager[_outputTile]; var tile = mapGrid.GetTileRef(location);
mapGrid.SetTile(eventArgs.ClickLocation, new Tile(desiredTile.TileId)); var baseTurf = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId];
EntitySystem.Get<AudioSystem>().PlayAtCoords("/Audio/Items/genhit.ogg", eventArgs.ClickLocation);
}
if (HasBaseTurf(desiredTile, baseTurf.Name) && eventArgs.Target == null && stack.Use(1))
{
PlaceAt(mapGrid, location, desiredTile.TileId);
}
}
else if(HasBaseTurf(desiredTile, "space"))
{
mapGrid = _mapManager.CreateGrid(locationMap.MapId);
mapGrid.WorldPosition = locationMap.Position;
location = new EntityCoordinates(mapGrid.GridEntityId, Vector2.Zero);
PlaceAt(mapGrid, location, desiredTile.TileId, mapGrid.TileSize/2f);
}
} }

View File

@@ -192,7 +192,7 @@ namespace Content.Server.GameObjects.Components.Metabolism
return; return;
} }
var lungs = _body.GetMechanismBehaviors<LungBehaviorComponent>().ToArray(); var lungs = _body.GetMechanismBehaviors<LungBehavior>().ToArray();
var needs = NeedsAndDeficit(frameTime); var needs = NeedsAndDeficit(frameTime);
var used = 0f; var used = 0f;

View File

@@ -0,0 +1,166 @@
using System;
using Content.Server.Commands;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Players;
namespace Content.Server.GameObjects.Components.Mobs
{
[RegisterComponent]
[ComponentReference(typeof(SharedAlertsComponent))]
public sealed class ServerAlertsComponent : SharedAlertsComponent
{
protected override void Startup()
{
base.Startup();
if (EntitySystem.TryGet<WeightlessSystem>(out var weightlessSystem))
{
weightlessSystem.AddAlert(this);
}
else
{
Logger.WarningS("alert", "weightlesssystem not found");
}
}
public override void OnRemove()
{
if (EntitySystem.TryGet<WeightlessSystem>(out var weightlessSystem))
{
weightlessSystem.RemoveAlert(this);
}
else
{
Logger.WarningS("alert", "weightlesssystem not found");
}
base.OnRemove();
}
public override ComponentState GetComponentState()
{
return new AlertsComponentState(CreateAlertStatesArray());
}
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession session = null)
{
base.HandleNetworkMessage(message, netChannel, session);
if (session == null)
{
throw new ArgumentNullException(nameof(session));
}
switch (message)
{
case ClickAlertMessage msg:
{
var player = session.AttachedEntity;
if (player != Owner)
{
break;
}
// TODO: Implement clicking other status effects in the HUD
if (AlertManager.TryDecode(msg.EncodedAlert, out var alert))
{
PerformAlertClickCallback(alert, player);
}
else
{
Logger.WarningS("alert", "unrecognized encoded alert {0}", msg.EncodedAlert);
}
break;
}
}
}
}
public sealed class ShowAlert : IClientCommand
{
public string Command => "showalert";
public string Description => "Shows an alert for a player, defaulting to current player";
public string Help => "showalert <alertType> <severity, -1 if no severity> <name or userID, omit for current player>";
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
{
var attachedEntity = player.AttachedEntity;
if (args.Length > 2)
{
var target = args[2];
if (!Commands.CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
}
if (!CommandUtils.ValidateAttachedEntity(shell, player, attachedEntity)) return;
if (!attachedEntity.TryGetComponent(out ServerAlertsComponent alertsComponent))
{
shell.SendText(player, "user has no alerts component");
return;
}
var alertType = args[0];
var severity = args[1];
var alertMgr = IoCManager.Resolve<AlertManager>();
if (!alertMgr.TryGet(Enum.Parse<AlertType>(alertType), out var alert))
{
shell.SendText(player, "unrecognized alertType " + alertType);
return;
}
if (!short.TryParse(severity, out var sevint))
{
shell.SendText(player, "invalid severity " + sevint);
return;
}
alertsComponent.ShowAlert(alert.AlertType, sevint == -1 ? (short?) null : sevint);
}
}
public sealed class ClearAlert : IClientCommand
{
public string Command => "clearalert";
public string Description => "Clears an alert for a player, defaulting to current player";
public string Help => "clearalert <alertType> <name or userID, omit for current player>";
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
{
var attachedEntity = player.AttachedEntity;
if (args.Length > 1)
{
var target = args[1];
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
}
if (!CommandUtils.ValidateAttachedEntity(shell, player, attachedEntity)) return;
if (!attachedEntity.TryGetComponent(out ServerAlertsComponent alertsComponent))
{
shell.SendText(player, "user has no alerts component");
return;
}
var alertType = args[0];
var alertMgr = IoCManager.Resolve<AlertManager>();
if (!alertMgr.TryGet(Enum.Parse<AlertType>(alertType), out var alert))
{
shell.SendText(player, "unrecognized alertType " + alertType);
return;
}
alertsComponent.ClearAlert(alert.AlertType);
}
}
}

View File

@@ -1,152 +0,0 @@
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Atmos;
using Content.Server.GameObjects.Components.Buckle;
using Content.Server.GameObjects.Components.Movement;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Pulling;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interfaces;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Players;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Mobs
{
[RegisterComponent]
[ComponentReference(typeof(SharedStatusEffectsComponent))]
public sealed class ServerStatusEffectsComponent : SharedStatusEffectsComponent
{
[ViewVariables]
private readonly Dictionary<StatusEffect, StatusEffectStatus> _statusEffects = new Dictionary<StatusEffect, StatusEffectStatus>();
public override IReadOnlyDictionary<StatusEffect, StatusEffectStatus> Statuses => _statusEffects;
protected override void Startup()
{
base.Startup();
EntitySystem.Get<WeightlessSystem>().AddStatus(this);
}
public override void OnRemove()
{
EntitySystem.Get<WeightlessSystem>().RemoveStatus(this);
base.OnRemove();
}
public override ComponentState GetComponentState()
{
return new StatusEffectComponentState(_statusEffects);
}
public override void ChangeStatusEffectIcon(StatusEffect effect, string icon)
{
if (_statusEffects.TryGetValue(effect, out var value) && value.Icon == icon)
{
return;
}
_statusEffects[effect] = new StatusEffectStatus()
{Icon = icon, Cooldown = value.Cooldown};
Dirty();
}
public void ChangeStatusEffectCooldown(StatusEffect effect, ValueTuple<TimeSpan, TimeSpan> cooldown)
{
if (_statusEffects.TryGetValue(effect, out var value)
&& value.Cooldown == cooldown)
{
return;
}
_statusEffects[effect] = new StatusEffectStatus()
{
Icon = value.Icon, Cooldown = cooldown
};
Dirty();
}
public override void ChangeStatusEffect(StatusEffect effect, string icon, ValueTuple<TimeSpan, TimeSpan>? cooldown)
{
_statusEffects[effect] = new StatusEffectStatus()
{Icon = icon, Cooldown = cooldown};
Dirty();
}
public override void RemoveStatusEffect(StatusEffect effect)
{
if (!_statusEffects.Remove(effect))
{
return;
}
Dirty();
}
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession session = null)
{
base.HandleNetworkMessage(message, netChannel, session);
if (session == null)
{
throw new ArgumentNullException(nameof(session));
}
switch (message)
{
case ClickStatusMessage msg:
{
var player = session.AttachedEntity;
if (player != Owner)
{
break;
}
// TODO: Implement clicking other status effects in the HUD
switch (msg.Effect)
{
case StatusEffect.Buckled:
if (!player.TryGetComponent(out BuckleComponent buckle))
break;
buckle.TryUnbuckle(player);
break;
case StatusEffect.Piloting:
if (!player.TryGetComponent(out ShuttleControllerComponent controller))
break;
controller.RemoveController();
break;
case StatusEffect.Pulling:
EntitySystem
.Get<SharedPullingSystem>()
.GetPulled(player)?
.GetComponentOrNull<SharedPullableComponent>()?
.TryStopPull();
break;
case StatusEffect.Fire:
if (!player.TryGetComponent(out FlammableComponent flammable))
break;
flammable.Resist();
break;
default:
player.PopupMessage(msg.Effect.ToString());
break;
}
break;
}
}
}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Mobs.State; using Content.Shared.GameObjects.Components.Mobs.State;
@@ -17,10 +18,9 @@ namespace Content.Server.GameObjects.Components.Mobs.State
appearance.SetData(DamageStateVisuals.State, DamageState.Critical); appearance.SetData(DamageStateVisuals.State, DamageState.Critical);
} }
if (entity.TryGetComponent(out ServerStatusEffectsComponent status)) if (entity.TryGetComponent(out ServerAlertsComponent status))
{ {
status.ChangeStatusEffectIcon(StatusEffect.Health, status.ShowAlert(AlertType.HumanCrit); //Todo: combine humancrit-0 and humancrit-1 into a gif and display it
"/Textures/Interface/StatusEffects/Human/humancrit-0.png"); //Todo: combine humancrit-0 and humancrit-1 into a gif and display it
} }
if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlay)) if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlay))

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Mobs.State; using Content.Shared.GameObjects.Components.Mobs.State;
@@ -18,10 +19,9 @@ namespace Content.Server.GameObjects.Components.Mobs.State
appearance.SetData(DamageStateVisuals.State, DamageState.Dead); appearance.SetData(DamageStateVisuals.State, DamageState.Dead);
} }
if (entity.TryGetComponent(out ServerStatusEffectsComponent status)) if (entity.TryGetComponent(out ServerAlertsComponent status))
{ {
status.ChangeStatusEffectIcon(StatusEffect.Health, status.ShowAlert(AlertType.HumanDead);
"/Textures/Interface/StatusEffects/Human/humandead.png");
} }
if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlayComponent)) if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlayComponent))

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Mobs.State; using Content.Shared.GameObjects.Components.Mobs.State;
@@ -51,9 +52,9 @@ namespace Content.Server.GameObjects.Components.Mobs.State
// TODO: Might want to add an OnRemove() to IMobState since those are where these components are being used // TODO: Might want to add an OnRemove() to IMobState since those are where these components are being used
base.OnRemove(); base.OnRemove();
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status)) if (Owner.TryGetComponent(out ServerAlertsComponent status))
{ {
status.RemoveStatusEffect(StatusEffect.Health); status.ClearAlert(AlertType.HumanHealth);
} }
if (Owner.TryGetComponent(out ServerOverlayEffectsComponent overlay)) if (Owner.TryGetComponent(out ServerOverlayEffectsComponent overlay))

View File

@@ -1,5 +1,6 @@
using Content.Server.GameObjects.Components.Damage; using Content.Server.GameObjects.Components.Damage;
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Mobs.State; using Content.Shared.GameObjects.Components.Mobs.State;
@@ -27,15 +28,14 @@ namespace Content.Server.GameObjects.Components.Mobs.State
public override void UpdateState(IEntity entity) public override void UpdateState(IEntity entity)
{ {
if (!entity.TryGetComponent(out ServerStatusEffectsComponent status)) if (!entity.TryGetComponent(out ServerAlertsComponent status))
{ {
return; return;
} }
if (!entity.TryGetComponent(out IDamageableComponent damageable)) if (!entity.TryGetComponent(out IDamageableComponent damageable))
{ {
status.ChangeStatusEffectIcon(StatusEffect.Health, status.ShowAlert(AlertType.HumanHealth, 0);
"/Textures/Interface/StatusEffects/Human/human0.png");
return; return;
} }
@@ -49,10 +49,9 @@ namespace Content.Server.GameObjects.Components.Mobs.State
return; return;
} }
var modifier = (int) (ruinable.TotalDamage / (threshold / 7f)); var modifier = (short) (ruinable.TotalDamage / (threshold / 7f));
status.ChangeStatusEffectIcon(StatusEffect.Health, status.ShowAlert(AlertType.HumanHealth, modifier);
"/Textures/Interface/StatusEffects/Human/human" + modifier + ".png");
break; break;
} }
@@ -63,10 +62,9 @@ namespace Content.Server.GameObjects.Components.Mobs.State
return; return;
} }
var modifier = (int) (damageable.TotalDamage / (threshold / 7f)); var modifier = (short) (damageable.TotalDamage / (threshold / 7f));
status.ChangeStatusEffectIcon(StatusEffect.Health, status.ShowAlert(AlertType.HumanHealth, modifier);
"/Textures/Interface/StatusEffects/Human/human" + modifier + ".png");
break; break;
} }
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.Components.Movement;
@@ -89,7 +90,7 @@ namespace Content.Server.GameObjects.Components.Mobs
} }
if (!StunStart.HasValue || !StunEnd.HasValue || if (!StunStart.HasValue || !StunEnd.HasValue ||
!Owner.TryGetComponent(out ServerStatusEffectsComponent status)) !Owner.TryGetComponent(out ServerAlertsComponent status))
{ {
return; return;
} }
@@ -102,7 +103,7 @@ namespace Content.Server.GameObjects.Components.Mobs
if (progress >= length) if (progress >= length)
{ {
Owner.SpawnTimer(250, () => status.RemoveStatusEffect(StatusEffect.Stun), StatusRemoveCancellation.Token); Owner.SpawnTimer(250, () => status.ClearAlert(AlertType.Stun), StatusRemoveCancellation.Token);
LastStun = null; LastStun = null;
} }
} }

View File

@@ -3,6 +3,7 @@ using Content.Server.GameObjects.Components.Items.Storage;
using Content.Shared.GameObjects.Components.Morgue; using Content.Shared.GameObjects.Components.Morgue;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Verbs; using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -52,9 +53,20 @@ namespace Content.Server.GameObjects.Components.Morgue
} }
} }
public override bool CanOpen(IEntity user, bool silent = false)
{
if (Cooking)
{
if (!silent) Owner.PopupMessage(user, Loc.GetString("Safety first, not while it's active!"));
return false;
}
return base.CanOpen(user, silent);
}
public void Cremate() public void Cremate()
{ {
if (Cooking) return; if (Cooking) return;
if (Open) return;
Appearance?.SetData(CrematoriumVisuals.Burning, true); Appearance?.SetData(CrematoriumVisuals.Burning, true);
Cooking = true; Cooking = true;
@@ -64,15 +76,18 @@ namespace Content.Server.GameObjects.Components.Morgue
Appearance?.SetData(CrematoriumVisuals.Burning, false); Appearance?.SetData(CrematoriumVisuals.Burning, false);
Cooking = false; Cooking = false;
for (var i = Contents.ContainedEntities.Count - 1; i >= 0; i--) if (Contents.ContainedEntities.Count > 0)
{ {
var item = Contents.ContainedEntities[i]; for (var i = Contents.ContainedEntities.Count - 1; i >= 0; i--)
Contents.Remove(item); {
item.Delete(); var item = Contents.ContainedEntities[i];
} Contents.Remove(item);
item.Delete();
}
var ash = Owner.EntityManager.SpawnEntity("Ash", Owner.Transform.Coordinates); var ash = Owner.EntityManager.SpawnEntity("Ash", Owner.Transform.Coordinates);
Contents.Insert(ash); Contents.Insert(ash);
}
TryOpenStorage(Owner); TryOpenStorage(Owner);
@@ -85,7 +100,7 @@ namespace Content.Server.GameObjects.Components.Morgue
{ {
protected override void GetData(IEntity user, CrematoriumEntityStorageComponent component, VerbData data) protected override void GetData(IEntity user, CrematoriumEntityStorageComponent component, VerbData data)
{ {
if (!ActionBlockerSystem.CanInteract(user) || component.Cooking) if (!ActionBlockerSystem.CanInteract(user) || component.Cooking || component.Open)
{ {
data.Visibility = VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
return; return;

View File

@@ -1,6 +1,9 @@
using Content.Shared.GameObjects.Components.Movement; #nullable enable
using Content.Shared.GameObjects.Components.Buckle;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.Physics; using Content.Shared.Physics;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths; using Robust.Shared.Maths;
namespace Content.Server.GameObjects.Components.Movement namespace Content.Server.GameObjects.Components.Movement
@@ -8,8 +11,8 @@ namespace Content.Server.GameObjects.Components.Movement
[RegisterComponent] [RegisterComponent]
public class ClimbingComponent : SharedClimbingComponent public class ClimbingComponent : SharedClimbingComponent
{ {
private bool _isClimbing = false; private bool _isClimbing;
private ClimbController _climbController = default; private ClimbController? _climbController;
public override bool IsClimbing public override bool IsClimbing
{ {
@@ -29,6 +32,19 @@ namespace Content.Server.GameObjects.Components.Movement
} }
} }
public override void HandleMessage(ComponentMessage message, IComponent? component)
{
base.HandleMessage(message, component);
switch (message)
{
case BuckleMessage msg:
if (msg.Buckled)
IsClimbing = false;
break;
}
}
/// <summary> /// <summary>
/// Make the owner climb from one point to another /// Make the owner climb from one point to another
/// </summary> /// </summary>

View File

@@ -1,6 +1,7 @@
#nullable enable #nullable enable
using Content.Server.GameObjects.Components.Buckle; using Content.Server.GameObjects.Components.Buckle;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.Components.Strap; using Content.Shared.GameObjects.Components.Strap;
@@ -31,9 +32,9 @@ namespace Content.Server.GameObjects.Components.Movement
private bool _movingRight; private bool _movingRight;
/// <summary> /// <summary>
/// The icon to be displayed when piloting from this chair. /// ID of the alert to show when piloting
/// </summary> /// </summary>
private string _pilotingIcon = default!; private AlertType _pilotingAlertType;
/// <summary> /// <summary>
/// The entity that's currently controlling this component. /// The entity that's currently controlling this component.
@@ -137,7 +138,7 @@ namespace Content.Server.GameObjects.Components.Movement
if (_controller != null || if (_controller != null ||
!entity.TryGetComponent(out MindComponent? mind) || !entity.TryGetComponent(out MindComponent? mind) ||
mind.Mind == null || mind.Mind == null ||
!Owner.TryGetComponent(out ServerStatusEffectsComponent? status)) !Owner.TryGetComponent(out ServerAlertsComponent? status))
{ {
return; return;
} }
@@ -145,7 +146,15 @@ namespace Content.Server.GameObjects.Components.Movement
mind.Mind.Visit(Owner); mind.Mind.Visit(Owner);
_controller = entity; _controller = entity;
status.ChangeStatusEffectIcon(StatusEffect.Piloting, _pilotingIcon); status.ShowAlert(_pilotingAlertType, onClickAlert: OnClickAlert);
}
private void OnClickAlert(ClickAlertEventArgs args)
{
if (args.Player.TryGetComponent(out ShuttleControllerComponent? controller))
{
controller.RemoveController();
}
} }
/// <summary> /// <summary>
@@ -177,9 +186,9 @@ namespace Content.Server.GameObjects.Components.Movement
/// <param name="entity">The entity to update</param> /// <param name="entity">The entity to update</param>
private void UpdateRemovedEntity(IEntity entity) private void UpdateRemovedEntity(IEntity entity)
{ {
if (Owner.TryGetComponent(out ServerStatusEffectsComponent? status)) if (Owner.TryGetComponent(out ServerAlertsComponent? status))
{ {
status.RemoveStatusEffect(StatusEffect.Piloting); status.ClearAlert(_pilotingAlertType);
} }
if (entity.TryGetComponent(out MindComponent? mind)) if (entity.TryGetComponent(out MindComponent? mind))
@@ -211,13 +220,13 @@ namespace Content.Server.GameObjects.Components.Movement
{ {
base.ExposeData(serializer); base.ExposeData(serializer);
serializer.DataField(ref _pilotingIcon, "pilotingIcon", "/Textures/Interface/StatusEffects/Buckle/buckled.png"); serializer.DataField(ref _pilotingAlertType, "pilotingAlertType", AlertType.PilotingShuttle);
} }
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
Owner.EnsureComponent<ServerStatusEffectsComponent>(); Owner.EnsureComponent<ServerAlertsComponent>();
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -1,9 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.NodeContainer.Nodes; using Content.Server.GameObjects.Components.NodeContainer.Nodes;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.Maths;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -34,8 +31,6 @@ namespace Content.Server.GameObjects.Components.NodeContainer
{ {
node.Initialize(Owner); node.Initialize(Owner);
} }
Owner.EntityManager.EventBus.SubscribeEvent<RotateEvent>(EventSource.Local, this, RotateEvent);
} }
protected override void Startup() protected override void Startup()
@@ -55,16 +50,5 @@ namespace Content.Server.GameObjects.Components.NodeContainer
} }
base.OnRemove(); base.OnRemove();
} }
private void RotateEvent(RotateEvent ev)
{
if (ev.Sender != Owner || ev.NewRotation == ev.OldRotation)
return;
foreach (var rotatableNode in Nodes.OfType<IRotatableNode>())
{
rotatableNode.RotateEvent(ev);
}
}
} }
} }

View File

@@ -153,7 +153,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
} }
if (!target.TryGetComponent(out IBody body) || if (!target.TryGetComponent(out IBody body) ||
!body.TryGetMechanismBehaviors<StomachBehaviorComponent>(out var stomachs)) !body.TryGetMechanismBehaviors<StomachBehavior>(out var stomachs))
{ {
return false; return false;
} }

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.Body.Behavior;
using Content.Server.GameObjects.Components.Chemistry; using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Items.Storage;
@@ -133,7 +134,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
var trueTarget = target ?? user; var trueTarget = target ?? user;
if (!trueTarget.TryGetComponent(out IBody? body) || if (!trueTarget.TryGetComponent(out IBody? body) ||
!body.TryGetMechanismBehaviors<SharedStomachBehaviorComponent>(out var stomachs)) !body.TryGetMechanismBehaviors<StomachBehavior>(out var stomachs))
{ {
return false; return false;
} }

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.Alert;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
@@ -70,11 +71,11 @@ namespace Content.Server.GameObjects.Components.Nutrition
} }
public static readonly Dictionary<HungerThreshold, string> HungerThresholdImages = new Dictionary<HungerThreshold, string> public static readonly Dictionary<HungerThreshold, AlertType> HungerThresholdAlertTypes = new Dictionary<HungerThreshold, AlertType>
{ {
{ HungerThreshold.Overfed, "/Textures/Interface/StatusEffects/Hunger/Overfed.png" }, { HungerThreshold.Overfed, AlertType.Overfed },
{ HungerThreshold.Peckish, "/Textures/Interface/StatusEffects/Hunger/Peckish.png" }, { HungerThreshold.Peckish, AlertType.Peckish },
{ HungerThreshold.Starving, "/Textures/Interface/StatusEffects/Hunger/Starving.png" }, { HungerThreshold.Starving, AlertType.Starving },
}; };
public void HungerThresholdEffect(bool force = false) public void HungerThresholdEffect(bool force = false)
@@ -89,15 +90,15 @@ namespace Content.Server.GameObjects.Components.Nutrition
} }
// Update UI // Update UI
Owner.TryGetComponent(out ServerStatusEffectsComponent statusEffectsComponent); Owner.TryGetComponent(out ServerAlertsComponent alertsComponent);
if (HungerThresholdImages.TryGetValue(_currentHungerThreshold, out var statusTexture)) if (HungerThresholdAlertTypes.TryGetValue(_currentHungerThreshold, out var alertId))
{ {
statusEffectsComponent?.ChangeStatusEffectIcon(StatusEffect.Hunger, statusTexture); alertsComponent?.ShowAlert(alertId);
} }
else else
{ {
statusEffectsComponent?.RemoveStatusEffect(StatusEffect.Hunger); alertsComponent?.ClearAlertCategory(AlertCategory.Hunger);
} }
switch (_currentHungerThreshold) switch (_currentHungerThreshold)

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.Alert;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
@@ -62,11 +63,11 @@ namespace Content.Server.GameObjects.Components.Nutrition
{ThirstThreshold.Dead, 0.0f}, {ThirstThreshold.Dead, 0.0f},
}; };
public static readonly Dictionary<ThirstThreshold, string> ThirstThresholdImages = new Dictionary<ThirstThreshold, string> public static readonly Dictionary<ThirstThreshold, AlertType> ThirstThresholdAlertTypes = new Dictionary<ThirstThreshold, AlertType>
{ {
{ThirstThreshold.OverHydrated, "/Textures/Interface/StatusEffects/Thirst/OverHydrated.png"}, {ThirstThreshold.OverHydrated, AlertType.Overhydrated},
{ThirstThreshold.Thirsty, "/Textures/Interface/StatusEffects/Thirst/Thirsty.png"}, {ThirstThreshold.Thirsty, AlertType.Thirsty},
{ThirstThreshold.Parched, "/Textures/Interface/StatusEffects/Thirst/Parched.png"}, {ThirstThreshold.Parched, AlertType.Parched},
}; };
public override void ExposeData(ObjectSerializer serializer) public override void ExposeData(ObjectSerializer serializer)
@@ -87,15 +88,15 @@ namespace Content.Server.GameObjects.Components.Nutrition
} }
// Update UI // Update UI
Owner.TryGetComponent(out ServerStatusEffectsComponent statusEffectsComponent); Owner.TryGetComponent(out ServerAlertsComponent alertsComponent);
if (ThirstThresholdImages.TryGetValue(_currentThirstThreshold, out var statusTexture)) if (ThirstThresholdAlertTypes.TryGetValue(_currentThirstThreshold, out var alertId))
{ {
statusEffectsComponent?.ChangeStatusEffectIcon(StatusEffect.Thirst, statusTexture); alertsComponent?.ShowAlert(alertId);
} }
else else
{ {
statusEffectsComponent?.RemoveStatusEffect(StatusEffect.Thirst); alertsComponent?.ClearAlertCategory(AlertCategory.Thirst);
} }
switch (_currentThirstThreshold) switch (_currentThirstThreshold)

View File

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Access; using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Paper;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Interfaces.PDA; using Content.Server.Interfaces.PDA;
@@ -39,13 +40,19 @@ namespace Content.Server.GameObjects.Components.PDA
[Dependency] private readonly IPDAUplinkManager _uplinkManager = default!; [Dependency] private readonly IPDAUplinkManager _uplinkManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
[ViewVariables] private Container _idSlot = default!; [ViewVariables] private ContainerSlot _idSlot = default!;
[ViewVariables] private ContainerSlot _penSlot = default!;
[ViewVariables] private bool _lightOn; [ViewVariables] private bool _lightOn;
[ViewVariables] private string _startingIdCard = default!;
[ViewVariables] public bool IdSlotEmpty => _idSlot.ContainedEntities.Count < 1; [ViewVariables] private string? _startingIdCard = default!;
[ViewVariables] private string? _startingPen = default!;
[ViewVariables] public string? OwnerName { get; private set; } [ViewVariables] public string? OwnerName { get; private set; }
[ViewVariables] public IdCardComponent? ContainedID { get; private set; } [ViewVariables] public IdCardComponent? ContainedID { get; private set; }
[ViewVariables] public bool IdSlotEmpty => _idSlot.ContainedEntity == null;
[ViewVariables] public bool PenSlotEmpty => _penSlot.ContainedEntity == null;
[ViewVariables] private UplinkAccount? _syndicateUplinkAccount; [ViewVariables] private UplinkAccount? _syndicateUplinkAccount;
@@ -62,22 +69,34 @@ namespace Content.Server.GameObjects.Components.PDA
{ {
base.ExposeData(serializer); base.ExposeData(serializer);
serializer.DataField(ref _startingIdCard, "idCard", "AssistantIDCard"); serializer.DataField(ref _startingIdCard, "idCard", "AssistantIDCard");
serializer.DataField(ref _startingPen, "pen", "Pen");
} }
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
_idSlot = ContainerManagerComponent.Ensure<Container>("pda_entity_container", Owner, out var existed); _idSlot = ContainerManagerComponent.Ensure<ContainerSlot>("pda_entity_container", Owner);
_penSlot = ContainerManagerComponent.Ensure<ContainerSlot>("pda_pen_slot", Owner);
if (UserInterface != null) if (UserInterface != null)
{ {
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage; UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
} }
var idCard = _entityManager.SpawnEntity(_startingIdCard, Owner.Transform.Coordinates); if (!string.IsNullOrEmpty(_startingIdCard))
var idCardComponent = idCard.GetComponent<IdCardComponent>(); {
_idSlot.Insert(idCardComponent.Owner); var idCard = _entityManager.SpawnEntity(_startingIdCard, Owner.Transform.Coordinates);
ContainedID = idCardComponent; var idCardComponent = idCard.GetComponent<IdCardComponent>();
_idSlot.Insert(idCardComponent.Owner);
ContainedID = idCardComponent;
}
if (!string.IsNullOrEmpty(_startingPen))
{
var pen = _entityManager.SpawnEntity(_startingPen, Owner.Transform.Coordinates);
_penSlot.Insert(pen);
}
UpdatePDAAppearance(); UpdatePDAAppearance();
} }
@@ -85,23 +104,29 @@ namespace Content.Server.GameObjects.Components.PDA
{ {
switch (message.Message) switch (message.Message)
{ {
case PDARequestUpdateInterfaceMessage msg: case PDARequestUpdateInterfaceMessage _:
{ {
UpdatePDAUserInterface(); UpdatePDAUserInterface();
break; break;
} }
case PDAToggleFlashlightMessage msg: case PDAToggleFlashlightMessage _:
{ {
ToggleLight(); ToggleLight();
break; break;
} }
case PDAEjectIDMessage msg: case PDAEjectIDMessage _:
{ {
HandleIDEjection(message.Session.AttachedEntity!); HandleIDEjection(message.Session.AttachedEntity!);
break; break;
} }
case PDAEjectPenMessage _:
{
HandlePenEjection(message.Session.AttachedEntity!);
break;
}
case PDAUplinkBuyListingMessage buyMsg: case PDAUplinkBuyListingMessage buyMsg:
{ {
if (!_uplinkManager.TryPurchaseItem(_syndicateUplinkAccount, buyMsg.ItemId)) if (!_uplinkManager.TryPurchaseItem(_syndicateUplinkAccount, buyMsg.ItemId))
@@ -131,11 +156,11 @@ namespace Content.Server.GameObjects.Components.PDA
var accData = new UplinkAccountData(_syndicateUplinkAccount.AccountHolder, var accData = new UplinkAccountData(_syndicateUplinkAccount.AccountHolder,
_syndicateUplinkAccount.Balance); _syndicateUplinkAccount.Balance);
var listings = _uplinkManager.FetchListings.Values.ToArray(); var listings = _uplinkManager.FetchListings.Values.ToArray();
UserInterface?.SetState(new PDAUpdateState(_lightOn, ownerInfo, accData, listings)); UserInterface?.SetState(new PDAUpdateState(_lightOn, !PenSlotEmpty, ownerInfo, accData, listings));
} }
else else
{ {
UserInterface?.SetState(new PDAUpdateState(_lightOn, ownerInfo)); UserInterface?.SetState(new PDAUpdateState(_lightOn, !PenSlotEmpty, ownerInfo));
} }
UpdatePDAAppearance(); UpdatePDAAppearance();
@@ -150,14 +175,11 @@ namespace Content.Server.GameObjects.Components.PDA
} }
} }
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs) private bool TryInsertIdCard(InteractUsingEventArgs eventArgs, IdCardComponent idCardComponent)
{ {
var item = eventArgs.Using; var item = eventArgs.Using;
if (_idSlot.Contains(item))
if (!item.TryGetComponent<IdCardComponent>(out var idCardComponent) || _idSlot.Contains(item))
{
return false; return false;
}
if (!eventArgs.User.TryGetComponent(out IHandsComponent? hands)) if (!eventArgs.User.TryGetComponent(out IHandsComponent? hands))
{ {
@@ -177,17 +199,70 @@ namespace Content.Server.GameObjects.Components.PDA
return true; return true;
} }
InsertIdCard(idCardComponent);
if (swap != null) if (swap != null)
{ {
eventArgs.User.GetComponent<HandsComponent>().PutInHand(swap.GetComponent<ItemComponent>()); hands.PutInHand(swap.GetComponent<ItemComponent>());
} }
InsertIdCard(idCardComponent);
UpdatePDAUserInterface(); UpdatePDAUserInterface();
return true; return true;
} }
private bool TryInsertPen(InteractUsingEventArgs eventArgs)
{
var item = eventArgs.Using;
if (_penSlot.Contains(item))
return false;
if (!eventArgs.User.TryGetComponent(out IHandsComponent? hands))
{
Owner.PopupMessage(eventArgs.User, Loc.GetString("You have no hands!"));
return true;
}
IEntity? swap = null;
if (!PenSlotEmpty)
{
// Swap
swap = _penSlot.ContainedEntities[0];
}
if (!hands.Drop(item))
{
return true;
}
if (swap != null)
{
hands.PutInHand(swap.GetComponent<ItemComponent>());
}
// Insert Pen
_penSlot.Insert(item);
UpdatePDAUserInterface();
return true;
}
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
{
var item = eventArgs.Using;
if (item.TryGetComponent<IdCardComponent>(out var idCardComponent))
{
return TryInsertIdCard(eventArgs, idCardComponent);
}
if (item.HasComponent<WriteComponent>())
{
return TryInsertPen(eventArgs);
}
return false;
}
void IActivate.Activate(ActivateEventArgs eventArgs) void IActivate.Activate(ActivateEventArgs eventArgs)
{ {
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
@@ -273,6 +348,21 @@ namespace Content.Server.GameObjects.Components.PDA
UpdatePDAUserInterface(); UpdatePDAUserInterface();
} }
private void HandlePenEjection(IEntity pdaUser)
{
if (PenSlotEmpty)
return;
var pen = _penSlot.ContainedEntities[0];
_penSlot.Remove(pen);
var hands = pdaUser.GetComponent<HandsComponent>();
var itemComponent = pen.GetComponent<ItemComponent>();
hands.PutInHandOrDrop(itemComponent);
UpdatePDAUserInterface();
}
[Verb] [Verb]
public sealed class EjectIDVerb : Verb<PDAComponent> public sealed class EjectIDVerb : Verb<PDAComponent>
{ {
@@ -294,6 +384,28 @@ namespace Content.Server.GameObjects.Components.PDA
} }
} }
[Verb]
public sealed class EjectPenVerb : Verb<PDAComponent>
{
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
{
if (!ActionBlockerSystem.CanInteract(user))
{
data.Visibility = VerbVisibility.Invisible;
return;
}
data.Text = Loc.GetString("Eject Pen");
data.Visibility = component.PenSlotEmpty ? VerbVisibility.Invisible : VerbVisibility.Visible;
}
protected override void Activate(IEntity user, PDAComponent component)
{
component.HandlePenEjection(user);
}
}
[Verb]
public sealed class ToggleFlashlightVerb : Verb<PDAComponent> public sealed class ToggleFlashlightVerb : Verb<PDAComponent>
{ {
protected override void GetData(IEntity user, PDAComponent component, VerbData data) protected override void GetData(IEntity user, PDAComponent component, VerbData data)

View File

@@ -6,6 +6,8 @@ using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Rotatable namespace Content.Server.GameObjects.Components.Rotatable
{ {
@@ -14,9 +16,21 @@ namespace Content.Server.GameObjects.Components.Rotatable
{ {
public override string Name => "Rotatable"; public override string Name => "Rotatable";
/// <summary>
/// If true, this entity can be rotated even while anchored.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public bool RotateWhileAnchored { get; private set; }
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(this, x => x.RotateWhileAnchored, "rotateWhileAnchored", false);
}
private void TryRotate(IEntity user, Angle angle) private void TryRotate(IEntity user, Angle angle)
{ {
if (Owner.TryGetComponent(out IPhysicsComponent physics)) if (!RotateWhileAnchored && Owner.TryGetComponent(out IPhysicsComponent physics))
{ {
if (physics.Anchored) if (physics.Anchored)
{ {
@@ -33,7 +47,7 @@ namespace Content.Server.GameObjects.Components.Rotatable
{ {
protected override void GetData(IEntity user, RotatableComponent component, VerbData data) protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
{ {
if (!ActionBlockerSystem.CanInteract(user)) if (!ActionBlockerSystem.CanInteract(user) || (!component.RotateWhileAnchored && component.Owner.TryGetComponent(out IPhysicsComponent physics) && physics.Anchored))
{ {
data.Visibility = VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
return; return;
@@ -55,7 +69,7 @@ namespace Content.Server.GameObjects.Components.Rotatable
{ {
protected override void GetData(IEntity user, RotatableComponent component, VerbData data) protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
{ {
if (!ActionBlockerSystem.CanInteract(user)) if (!ActionBlockerSystem.CanInteract(user) || (!component.RotateWhileAnchored && component.Owner.TryGetComponent(out IPhysicsComponent physics) && physics.Anchored))
{ {
data.Visibility = VerbVisibility.Invisible; data.Visibility = VerbVisibility.Invisible;
return; return;

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.Buckle; using Content.Server.GameObjects.Components.Buckle;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Strap; using Content.Shared.GameObjects.Components.Strap;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Verbs; using Content.Shared.GameObjects.Verbs;
@@ -27,7 +28,7 @@ namespace Content.Server.GameObjects.Components.Strap
private StrapPosition _position; private StrapPosition _position;
private string _buckleSound = null!; private string _buckleSound = null!;
private string _unbuckleSound = null!; private string _unbuckleSound = null!;
private string _buckledIcon = null!; private AlertType _buckledAlertType;
/// <summary> /// <summary>
/// The angle in degrees to rotate the player by when they get strapped /// The angle in degrees to rotate the player by when they get strapped
@@ -65,10 +66,10 @@ namespace Content.Server.GameObjects.Components.Strap
public string UnbuckleSound => _unbuckleSound; public string UnbuckleSound => _unbuckleSound;
/// <summary> /// <summary>
/// The icon to be displayed as a status when buckled /// ID of the alert to show when buckled
/// </summary> /// </summary>
[ViewVariables] [ViewVariables]
public string BuckledIcon => _buckledIcon; public AlertType BuckledAlertType => _buckledAlertType;
/// <summary> /// <summary>
/// The sum of the sizes of all the buckled entities in this strap /// The sum of the sizes of all the buckled entities in this strap
@@ -137,7 +138,7 @@ namespace Content.Server.GameObjects.Components.Strap
serializer.DataField(ref _position, "position", StrapPosition.None); serializer.DataField(ref _position, "position", StrapPosition.None);
serializer.DataField(ref _buckleSound, "buckleSound", "/Audio/Effects/buckle.ogg"); serializer.DataField(ref _buckleSound, "buckleSound", "/Audio/Effects/buckle.ogg");
serializer.DataField(ref _unbuckleSound, "unbuckleSound", "/Audio/Effects/unbuckle.ogg"); serializer.DataField(ref _unbuckleSound, "unbuckleSound", "/Audio/Effects/unbuckle.ogg");
serializer.DataField(ref _buckledIcon, "buckledIcon", "/Textures/Interface/StatusEffects/Buckle/buckled.png"); serializer.DataField(ref _buckledAlertType, "buckledAlertType", AlertType.Buckled);
serializer.DataField(ref _rotation, "rotation", 0); serializer.DataField(ref _rotation, "rotation", 0);
var defaultSize = 100; var defaultSize = 100;

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.Alert;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
@@ -74,43 +75,43 @@ namespace Content.Server.GameObjects.Components.Temperature
damageType = DamageType.Cold; damageType = DamageType.Cold;
} }
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status)) if (Owner.TryGetComponent(out ServerAlertsComponent status))
{ {
switch(CurrentTemperature) switch(CurrentTemperature)
{ {
// Cold strong. // Cold strong.
case var t when t <= 260: case var t when t <= 260:
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/cold3.png", null); status.ShowAlert(AlertType.Cold, 3);
break; break;
// Cold mild. // Cold mild.
case var t when t <= 280 && t > 260: case var t when t <= 280 && t > 260:
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/cold2.png", null); status.ShowAlert(AlertType.Cold, 2);
break; break;
// Cold weak. // Cold weak.
case var t when t <= 292 && t > 280: case var t when t <= 292 && t > 280:
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/cold1.png", null); status.ShowAlert(AlertType.Cold, 1);
break; break;
// Safe. // Safe.
case var t when t <= 327 && t > 292: case var t when t <= 327 && t > 292:
status.RemoveStatusEffect(StatusEffect.Temperature); status.ClearAlertCategory(AlertCategory.Temperature);
break; break;
// Heat weak. // Heat weak.
case var t when t <= 335 && t > 327: case var t when t <= 335 && t > 327:
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/hot1.png", null); status.ShowAlert(AlertType.Hot, 1);
break; break;
// Heat mild. // Heat mild.
case var t when t <= 345 && t > 335: case var t when t <= 345 && t > 335:
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/hot2.png", null); status.ShowAlert(AlertType.Hot, 2);
break; break;
// Heat strong. // Heat strong.
case var t when t > 345: case var t when t > 345:
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/hot3.png", null); status.ShowAlert(AlertType.Hot, 3);
break; break;
} }
} }

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.GameObjects.Components.VendingMachines; using Content.Shared.GameObjects.Components.VendingMachines;
@@ -16,6 +17,7 @@ using Robust.Shared.Audio;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Timers; using Robust.Shared.GameObjects.Components.Timers;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -146,7 +148,7 @@ namespace Content.Server.GameObjects.Components.VendingMachines
switch (message) switch (message)
{ {
case VendingMachineEjectMessage msg: case VendingMachineEjectMessage msg:
TryEject(msg.ID); TryEject(msg.ID, serverMsg.Session.AttachedEntity);
break; break;
case InventorySyncRequestMessage _: case InventorySyncRequestMessage _:
UserInterface?.SendMessage(new VendingMachineInventoryMessage(Inventory)); UserInterface?.SendMessage(new VendingMachineInventoryMessage(Inventory));
@@ -195,6 +197,19 @@ namespace Content.Server.GameObjects.Components.VendingMachines
EntitySystem.Get<AudioSystem>().PlayFromEntity(_soundVend, Owner, AudioParams.Default.WithVolume(-2f)); EntitySystem.Get<AudioSystem>().PlayFromEntity(_soundVend, Owner, AudioParams.Default.WithVolume(-2f));
} }
private void TryEject(string id, IEntity? sender)
{
if (Owner.TryGetComponent<AccessReader>(out var accessReader))
{
if (sender == null || !accessReader.IsAllowed(sender))
{
FlickDenyAnimation();
return;
}
}
TryEject(id);
}
private void FlickDenyAnimation() private void FlickDenyAnimation()
{ {
TrySetVisualState(VendingMachineVisualState.Deny); TrySetVisualState(VendingMachineVisualState.Deny);

View File

@@ -1,9 +1,9 @@
#nullable enable #nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.GameObjects.Components.Movement; using Content.Server.GameObjects.Components.Movement;
using Content.Shared;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.Components.Movement;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -44,7 +44,6 @@ namespace Content.Server.GameObjects.EntitySystems.AI
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
_configurationManager.RegisterCVar("ai.maxupdates", 64);
SubscribeLocalEvent<SleepAiMessage>(HandleAiSleep); SubscribeLocalEvent<SleepAiMessage>(HandleAiSleep);
var processors = _reflectionManager.GetAllChildren<AiLogicProcessor>(); var processors = _reflectionManager.GetAllChildren<AiLogicProcessor>();
@@ -60,7 +59,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI
/// <inheritdoc /> /// <inheritdoc />
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
var cvarMaxUpdates = _configurationManager.GetCVar<int>("ai.maxupdates"); var cvarMaxUpdates = _configurationManager.GetCVar(CCVars.AIMaxUpdates);
if (cvarMaxUpdates <= 0) if (cvarMaxUpdates <= 0)
return; return;

View File

@@ -328,9 +328,16 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding
return; return;
} }
var newGridId = moveEvent.NewPosition.GetGridId(_entityManager);
if (newGridId == GridId.Invalid)
{
HandleEntityRemove(moveEvent.Sender);
return;
}
// The pathfinding graph is tile-based so first we'll check if they're on a different tile and if we need to update. // The pathfinding graph is tile-based so first we'll check if they're on a different tile and if we need to update.
// If you get entities bigger than 1 tile wide you'll need some other system so god help you. // If you get entities bigger than 1 tile wide you'll need some other system so god help you.
var newTile = _mapManager.GetGrid(moveEvent.NewPosition.GetGridId(_entityManager)).GetTileRef(moveEvent.NewPosition); var newTile = _mapManager.GetGrid(newGridId).GetTileRef(moveEvent.NewPosition);
if (oldNode == null || oldNode.TileRef == newTile) if (oldNode == null || oldNode.TileRef == newTile)
{ {

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Content.Server.GameObjects.Components.Atmos; using Content.Server.GameObjects.Components.Atmos;
using Content.Server.Atmos; using Content.Server.Atmos;
using Content.Shared;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.GameObjects.EntitySystems.Atmos; using Content.Shared.GameObjects.EntitySystems.Atmos;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -47,7 +48,6 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
_atmosphereSystem = Get<AtmosphereSystem>(); _atmosphereSystem = Get<AtmosphereSystem>();
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged; _playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
_configManager.RegisterCVar("net.atmosdbgoverlaytickrate", 3.0f);
} }
public override void Shutdown() public override void Shutdown()
@@ -89,7 +89,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
AccumulatedFrameTime += frameTime; AccumulatedFrameTime += frameTime;
_updateCooldown = 1 / _configManager.GetCVar<float>("net.atmosdbgoverlaytickrate"); _updateCooldown = 1 / _configManager.GetCVar(CCVars.NetAtmosDebugOverlayTickRate);
if (AccumulatedFrameTime < _updateCooldown) if (AccumulatedFrameTime < _updateCooldown)
{ {

View File

@@ -4,12 +4,14 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Content.Server.GameObjects.Components.Atmos; using Content.Server.GameObjects.Components.Atmos;
using Content.Shared;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.GameObjects.EntitySystems.Atmos; using Content.Shared.GameObjects.EntitySystems.Atmos;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
@@ -65,7 +67,6 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
_atmosphereSystem = Get<AtmosphereSystem>(); _atmosphereSystem = Get<AtmosphereSystem>();
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged; _playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
_mapManager.OnGridRemoved += OnGridRemoved; _mapManager.OnGridRemoved += OnGridRemoved;
_configManager.RegisterCVar("net.gasoverlaytickrate", 3.0f);
} }
public override void Shutdown() public override void Shutdown()
@@ -228,14 +229,14 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
AccumulatedFrameTime += frameTime; AccumulatedFrameTime += frameTime;
_updateCooldown = 1 / _configManager.GetCVar<float>("net.gasoverlaytickrate"); _updateCooldown = 1 / _configManager.GetCVar(CCVars.NetGasOverlayTickRate);
if (AccumulatedFrameTime < _updateCooldown) if (AccumulatedFrameTime < _updateCooldown)
{ {
return; return;
} }
_updateRange = _configManager.GetCVar<float>("net.maxupdaterange") + RangeOffset; _updateRange = _configManager.GetCVar(CVars.NetMaxUpdateRange) + RangeOffset;
// TODO: So in the worst case scenario we still have to send a LOT of tile data per tick if there's a fire. // TODO: So in the worst case scenario we still have to send a LOT of tile data per tick if there's a fire.
// If we go with say 15 tile radius then we have up to 900 tiles to update per tick. // If we go with say 15 tile radius then we have up to 900 tiles to update per tick.

View File

@@ -1,7 +1,12 @@
using Content.Server.GameObjects.Components.Buckle; #nullable enable
using Content.Server.GameObjects.Components.Buckle;
using Content.Server.GameObjects.Components.Strap;
using Content.Server.GameObjects.EntitySystems.Click; using Content.Server.GameObjects.EntitySystems.Click;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystemMessages;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems namespace Content.Server.GameObjects.EntitySystems
@@ -15,13 +20,84 @@ namespace Content.Server.GameObjects.EntitySystems
UpdatesAfter.Add(typeof(InteractionSystem)); UpdatesAfter.Add(typeof(InteractionSystem));
UpdatesAfter.Add(typeof(InputSystem)); UpdatesAfter.Add(typeof(InputSystem));
SubscribeLocalEvent<MoveEvent>(MoveEvent);
SubscribeLocalEvent<EntInsertedIntoContainerMessage>(ContainerModified);
SubscribeLocalEvent<EntRemovedFromContainerMessage>(ContainerModified);
} }
public override void Update(float frameTime) public override void Shutdown()
{ {
foreach (var buckle in ComponentManager.EntityQuery<BuckleComponent>()) base.Shutdown();
UnsubscribeLocalEvent<MoveEvent>();
}
private void MoveEvent(MoveEvent ev)
{
if (!ev.Sender.TryGetComponent(out BuckleComponent? buckle))
{ {
buckle.Update(); return;
}
var strap = buckle.BuckledTo;
if (strap == null)
{
return;
}
var strapPosition = strap.Owner.Transform.Coordinates.Offset(buckle.BuckleOffset);
if (ev.NewPosition.InRange(EntityManager, strapPosition, 0.2f))
{
return;
}
buckle.TryUnbuckle(buckle.Owner, true);
}
private void ContainerModified(ContainerModifiedMessage message)
{
// Not returning is necessary in case an entity has both a buckle and strap component
if (message.Entity.TryGetComponent(out BuckleComponent? buckle))
{
ContainerModifiedReAttach(buckle, buckle.BuckledTo);
}
if (message.Entity.TryGetComponent(out StrapComponent? strap))
{
foreach (var buckledEntity in strap.BuckledEntities)
{
if (!buckledEntity.TryGetComponent(out BuckleComponent? buckled))
{
continue;
}
ContainerModifiedReAttach(buckled, strap);
}
}
}
private void ContainerModifiedReAttach(BuckleComponent buckle, StrapComponent? strap)
{
if (strap == null)
{
return;
}
var contained = ContainerHelpers.TryGetContainer(buckle.Owner, out var ownContainer);
var strapContained = ContainerHelpers.TryGetContainer(strap.Owner, out var strapContainer);
if (contained != strapContained || ownContainer != strapContainer)
{
buckle.TryUnbuckle(buckle.Owner, true);
return;
}
if (!contained)
{
buckle.ReAttach(strap);
} }
} }
} }

View File

@@ -156,7 +156,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
private bool HandleWideAttack(ICommonSession session, EntityCoordinates coords, EntityUid uid) private bool HandleWideAttack(ICommonSession session, EntityCoordinates coords, EntityUid uid)
{ {
// client sanitization // client sanitization
if (!_mapManager.GridExists(coords.GetGridId(_entityManager))) if (!coords.IsValid(_entityManager))
{ {
Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}"); Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}");
return true; return true;
@@ -211,7 +211,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
private bool HandleClientUseItemInHand(ICommonSession session, EntityCoordinates coords, EntityUid uid) private bool HandleClientUseItemInHand(ICommonSession session, EntityCoordinates coords, EntityUid uid)
{ {
// client sanitization // client sanitization
if (!_mapManager.GridExists(coords.GetGridId(_entityManager))) if (!coords.IsValid(_entityManager))
{ {
Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}"); Logger.InfoS("system.interaction", $"Invalid Coordinates: client={session}, coords={coords}");
return true; return true;
@@ -242,7 +242,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
private bool HandleTryPullObject(ICommonSession session, EntityCoordinates coords, EntityUid uid) private bool HandleTryPullObject(ICommonSession session, EntityCoordinates coords, EntityUid uid)
{ {
// client sanitization // client sanitization
if (!_mapManager.GridExists(coords.GetGridId(_entityManager))) if (!coords.IsValid(_entityManager))
{ {
Logger.InfoS("system.interaction", $"Invalid Coordinates for pulling: client={session}, coords={coords}"); Logger.InfoS("system.interaction", $"Invalid Coordinates for pulling: client={session}, coords={coords}");
return false; return false;
@@ -303,7 +303,7 @@ namespace Content.Server.GameObjects.EntitySystems.Click
} }
// Verify player is on the same map as the entity he clicked on // Verify player is on the same map as the entity he clicked on
if (_mapManager.GetGrid(coordinates.GetGridId(EntityManager)).ParentMapId != playerTransform.MapID) if (coordinates.GetMapId(_entityManager) != playerTransform.MapID)
{ {
Logger.WarningS("system.interaction", Logger.WarningS("system.interaction",
$"Player named {player.Name} clicked on a map he isn't located on"); $"Player named {player.Name} clicked on a map he isn't located on");

View File

@@ -20,6 +20,7 @@ using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -306,8 +307,18 @@ namespace Content.Server.GameObjects.EntitySystems
private async void HandleStartItemConstruction(TryStartItemConstructionMessage ev, EntitySessionEventArgs args) private async void HandleStartItemConstruction(TryStartItemConstructionMessage ev, EntitySessionEventArgs args)
{ {
var constructionPrototype = _prototypeManager.Index<ConstructionPrototype>(ev.PrototypeName); if (!_prototypeManager.TryIndex(ev.PrototypeName, out ConstructionPrototype constructionPrototype))
var constructionGraph = _prototypeManager.Index<ConstructionGraphPrototype>(constructionPrototype.Graph); {
Logger.Error($"Tried to start construction of invalid recipe '{ev.PrototypeName}'!");
return;
}
if (!_prototypeManager.TryIndex(constructionPrototype.Graph, out ConstructionGraphPrototype constructionGraph))
{
Logger.Error($"Invalid construction graph '{constructionPrototype.Graph}' in recipe '{ev.PrototypeName}'!");
return;
}
var startNode = constructionGraph.Nodes[constructionPrototype.StartNode]; var startNode = constructionGraph.Nodes[constructionPrototype.StartNode];
var targetNode = constructionGraph.Nodes[constructionPrototype.TargetNode]; var targetNode = constructionGraph.Nodes[constructionPrototype.TargetNode];
var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name); var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name);
@@ -352,8 +363,20 @@ namespace Content.Server.GameObjects.EntitySystems
private async void HandleStartStructureConstruction(TryStartStructureConstructionMessage ev, EntitySessionEventArgs args) private async void HandleStartStructureConstruction(TryStartStructureConstructionMessage ev, EntitySessionEventArgs args)
{ {
var constructionPrototype = _prototypeManager.Index<ConstructionPrototype>(ev.PrototypeName); if (!_prototypeManager.TryIndex(ev.PrototypeName, out ConstructionPrototype constructionPrototype))
var constructionGraph = _prototypeManager.Index<ConstructionGraphPrototype>(constructionPrototype.Graph); {
Logger.Error($"Tried to start construction of invalid recipe '{ev.PrototypeName}'!");
RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack));
return;
}
if (!_prototypeManager.TryIndex(constructionPrototype.Graph, out ConstructionGraphPrototype constructionGraph))
{
Logger.Error($"Invalid construction graph '{constructionPrototype.Graph}' in recipe '{ev.PrototypeName}'!");
RaiseNetworkEvent(new AckStructureConstructionMessage(ev.Ack));
return;
}
var startNode = constructionGraph.Nodes[constructionPrototype.StartNode]; var startNode = constructionGraph.Nodes[constructionPrototype.StartNode];
var targetNode = constructionGraph.Nodes[constructionPrototype.TargetNode]; var targetNode = constructionGraph.Nodes[constructionPrototype.TargetNode];
var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name); var pathFind = constructionGraph.Path(startNode.Name, targetNode.Name);

View File

@@ -1,27 +1,16 @@
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.IoC; using Robust.Shared.IoC;
namespace Content.Server.GameObjects.EntitySystems.DeviceNetwork namespace Content.Server.GameObjects.EntitySystems.DeviceNetwork
{ {
public class DeviceNetworkSystem : EntitySystem internal sealed class DeviceNetworkSystem : EntitySystem
{ {
private IDeviceNetwork _network; [Dependency] private readonly IDeviceNetwork _network = default!;
public override void Initialize()
{
base.Initialize();
_network = IoCManager.Resolve<IDeviceNetwork>();
}
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
base.Update(frameTime); base.Update(frameTime);
if (_network == null)
return;
//(ノ°Д°)ノ︵ ┻━┻
_network.Update(); _network.Update();
} }
} }

View File

@@ -1,28 +0,0 @@
using Content.Shared.GameObjects.Components.Body.Behavior;
using Content.Shared.GameObjects.EntitySystems;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class HeartSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
UpdatesBefore.Add(typeof(MetabolismSystem));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var heart in ComponentManager.EntityQuery<SharedHeartBehaviorComponent>())
{
heart.Update(frameTime);
}
}
}
}

View File

@@ -1,28 +0,0 @@
using Content.Shared.GameObjects.Components.Body.Behavior;
using Content.Shared.GameObjects.EntitySystems;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class LungSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
UpdatesBefore.Add(typeof(MetabolismSystem));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var lung in ComponentManager.EntityQuery<SharedLungBehaviorComponent>())
{
lung.Update(frameTime);
}
}
}
}

View File

@@ -0,0 +1,45 @@
using System.Linq;
using Content.Server.GameObjects.Components.NodeContainer;
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class NodeContainerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RotateEvent>(RotateEvent);
}
public override void Shutdown()
{
base.Shutdown();
UnsubscribeLocalEvent<RotateEvent>();
}
private void RotateEvent(RotateEvent ev)
{
if (!ev.Sender.TryGetComponent(out NodeContainerComponent container))
{
return;
}
if (ev.NewRotation == ev.OldRotation)
{
return;
}
foreach (var rotatableNode in container.Nodes.OfType<IRotatableNode>())
{
rotatableNode.RotateEvent(ev);
}
}
}
}

View File

@@ -6,6 +6,7 @@ using Content.Server.Players;
using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Input; using Content.Shared.Input;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
using Content.Shared.Utility;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects.Components; using Robust.Server.GameObjects.Components;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
@@ -75,9 +76,9 @@ namespace Content.Server.GameObjects.EntitySystems
} }
} }
public bool InRange(EntityCoordinates from, EntityCoordinates to) public bool InRange(IEntity pointer, EntityCoordinates coordinates)
{ {
return from.InRange(EntityManager, to, 15); return pointer.InRangeUnOccluded(coordinates, 15, e => e == pointer);
} }
public bool TryPoint(ICommonSession? session, EntityCoordinates coords, EntityUid uid) public bool TryPoint(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
@@ -100,7 +101,7 @@ namespace Content.Server.GameObjects.EntitySystems
return false; return false;
} }
if (!InRange(coords, player.Transform.Coordinates)) if (!InRange(player, coords))
{ {
player.PopupMessage(Loc.GetString("You can't reach there!")); player.PopupMessage(Loc.GetString("You can't reach there!"));
return false; return false;

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.GameObjects.Components.MachineLinking; using Content.Server.GameObjects.Components.MachineLinking;
using Content.Server.GameObjects.EntitySystems.Click;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Server.Interfaces.Console; using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
@@ -27,7 +28,7 @@ namespace Content.Server.GameObjects.EntitySystems
_transmitters = new Dictionary<NetUserId, SignalTransmitterComponent>(); _transmitters = new Dictionary<NetUserId, SignalTransmitterComponent>();
} }
public void SignalLinkerKeybind(NetUserId id, bool? enable) public bool SignalLinkerKeybind(NetUserId id, bool? enable)
{ {
if (enable == null) if (enable == null)
{ {
@@ -38,13 +39,13 @@ namespace Content.Server.GameObjects.EntitySystems
{ {
if (_transmitters.ContainsKey(id)) if (_transmitters.ContainsKey(id))
{ {
return; return true;
} }
if (_transmitters.Count == 0) if (_transmitters.Count == 0)
{ {
CommandBinds.Builder CommandBinds.Builder
.Bind(EngineKeyFunctions.Use, new PointerInputCmdHandler(HandleUse)) .BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(HandleUse), typeof(InteractionSystem))
.Register<SignalLinkerSystem>(); .Register<SignalLinkerSystem>();
} }
@@ -55,7 +56,7 @@ namespace Content.Server.GameObjects.EntitySystems
{ {
if (!_transmitters.ContainsKey(id)) if (!_transmitters.ContainsKey(id))
{ {
return; return false;
} }
_transmitters.Remove(id); _transmitters.Remove(id);
@@ -64,6 +65,7 @@ namespace Content.Server.GameObjects.EntitySystems
CommandBinds.Unregister<SignalLinkerSystem>(); CommandBinds.Unregister<SignalLinkerSystem>();
} }
} }
return enable == true;
} }
private bool HandleUse(ICommonSession session, EntityCoordinates coords, EntityUid uid) private bool HandleUse(ICommonSession session, EntityCoordinates coords, EntityUid uid)
@@ -132,7 +134,8 @@ namespace Content.Server.GameObjects.EntitySystems
return; return;
} }
system.SignalLinkerKeybind(player.UserId, enable); var ret = system.SignalLinkerKeybind(player.UserId, enable);
shell.SendText(player, ret ? "Enabled" : "Disabled");
} }
} }
} }

View File

@@ -4,11 +4,13 @@ using System.Text;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Interfaces.GameTicking; using Content.Server.Interfaces.GameTicking;
using Content.Server.StationEvents; using Content.Server.StationEvents;
using Content.Shared;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.Console; using Robust.Server.Console;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Network; using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Random;
using Robust.Shared.Interfaces.Reflection; using Robust.Shared.Interfaces.Reflection;
@@ -23,6 +25,7 @@ namespace Content.Server.GameObjects.EntitySystems.StationEvents
// Somewhat based off of TG's implementation of events // Somewhat based off of TG's implementation of events
public sealed class StationEventSystem : EntitySystem, IResettingEntitySystem public sealed class StationEventSystem : EntitySystem, IResettingEntitySystem
{ {
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IServerNetManager _netManager = default!; [Dependency] private readonly IServerNetManager _netManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTicker _gameTicker = default!; [Dependency] private readonly IGameTicker _gameTicker = default!;
@@ -164,6 +167,9 @@ namespace Content.Server.GameObjects.EntitySystems.StationEvents
_stationEvents.Add(stationEvent); _stationEvents.Add(stationEvent);
} }
// Can't just check debug / release for a default given mappers need to use release mode
// As such we'll always pause it by default.
_configurationManager.OnValueChanged(CCVars.EventsEnabled, value => Enabled = value, true);
_netManager.RegisterNetMessage<MsgGetStationEvents>(nameof(MsgGetStationEvents), GetEventReceived); _netManager.RegisterNetMessage<MsgGetStationEvents>(nameof(MsgGetStationEvents), GetEventReceived);
} }

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.Alert;
using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystemMessages.Gravity; using Content.Shared.GameObjects.EntitySystemMessages.Gravity;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
@@ -19,7 +20,7 @@ namespace Content.Server.GameObjects.EntitySystems
{ {
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
private readonly Dictionary<GridId, List<ServerStatusEffectsComponent>> _statuses = new Dictionary<GridId, List<ServerStatusEffectsComponent>>(); private readonly Dictionary<GridId, List<ServerAlertsComponent>> _alerts = new Dictionary<GridId, List<ServerAlertsComponent>>();
public override void Initialize() public override void Initialize()
{ {
@@ -31,15 +32,15 @@ namespace Content.Server.GameObjects.EntitySystems
public void Reset() public void Reset()
{ {
_statuses.Clear(); _alerts.Clear();
} }
public void AddStatus(ServerStatusEffectsComponent status) public void AddAlert(ServerAlertsComponent status)
{ {
var gridId = status.Owner.Transform.GridID; var gridId = status.Owner.Transform.GridID;
var statuses = _statuses.GetOrNew(gridId); var alerts = _alerts.GetOrNew(gridId);
statuses.Add(status); alerts.Add(status);
if (_mapManager.TryGetGrid(status.Owner.Transform.GridID, out var grid)) if (_mapManager.TryGetGrid(status.Owner.Transform.GridID, out var grid))
{ {
@@ -54,10 +55,10 @@ namespace Content.Server.GameObjects.EntitySystems
} }
} }
public void RemoveStatus(ServerStatusEffectsComponent status) public void RemoveAlert(ServerAlertsComponent status)
{ {
var grid = status.Owner.Transform.GridID; var grid = status.Owner.Transform.GridID;
if (!_statuses.TryGetValue(grid, out var statuses)) if (!_alerts.TryGetValue(grid, out var statuses))
{ {
return; return;
} }
@@ -67,7 +68,7 @@ namespace Content.Server.GameObjects.EntitySystems
private void GravityChanged(GravityChangedMessage ev) private void GravityChanged(GravityChangedMessage ev)
{ {
if (!_statuses.TryGetValue(ev.Grid.Index, out var statuses)) if (!_alerts.TryGetValue(ev.Grid.Index, out var statuses))
{ {
return; return;
} }
@@ -88,19 +89,19 @@ namespace Content.Server.GameObjects.EntitySystems
} }
} }
private void AddWeightless(ServerStatusEffectsComponent status) private void AddWeightless(ServerAlertsComponent status)
{ {
status.ChangeStatusEffect(StatusEffect.Weightless, "/Textures/Interface/StatusEffects/Weightless/weightless.png", null); status.ShowAlert(AlertType.Weightless);
} }
private void RemoveWeightless(ServerStatusEffectsComponent status) private void RemoveWeightless(ServerAlertsComponent status)
{ {
status.RemoveStatusEffect(StatusEffect.Weightless); status.ClearAlert(AlertType.Weightless);
} }
private void EntParentChanged(EntParentChangedMessage ev) private void EntParentChanged(EntParentChangedMessage ev)
{ {
if (!ev.Entity.TryGetComponent(out ServerStatusEffectsComponent status)) if (!ev.Entity.TryGetComponent(out ServerAlertsComponent status))
{ {
return; return;
} }
@@ -110,14 +111,14 @@ namespace Content.Server.GameObjects.EntitySystems
{ {
var oldGrid = mapGrid.GridIndex; var oldGrid = mapGrid.GridIndex;
if (_statuses.TryGetValue(oldGrid, out var oldStatuses)) if (_alerts.TryGetValue(oldGrid, out var oldStatuses))
{ {
oldStatuses.Remove(status); oldStatuses.Remove(status);
} }
} }
var newGrid = ev.Entity.Transform.GridID; var newGrid = ev.Entity.Transform.GridID;
var newStatuses = _statuses.GetOrNew(newGrid); var newStatuses = _alerts.GetOrNew(newGrid);
newStatuses.Add(status); newStatuses.Add(status);
} }

View File

@@ -9,6 +9,7 @@ using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameTicking; using Content.Server.Interfaces.GameTicking;
using Content.Server.Mobs.Roles.Suspicion; using Content.Server.Mobs.Roles.Suspicion;
using Content.Server.Players; using Content.Server.Players;
using Content.Shared;
using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.Components.PDA; using Content.Shared.GameObjects.Components.PDA;
using Content.Shared.Roles; using Content.Shared.Roles;
@@ -44,20 +45,12 @@ namespace Content.Server.GameTicking.GamePresets
private static string TraitorID = "SuspicionTraitor"; private static string TraitorID = "SuspicionTraitor";
private static string InnocentID = "SuspicionInnocent"; private static string InnocentID = "SuspicionInnocent";
public static void RegisterCVars(IConfigurationManager cfg)
{
cfg.RegisterCVar("game.suspicion_min_players", 5);
cfg.RegisterCVar("game.suspicion_min_traitors", 2);
cfg.RegisterCVar("game.suspicion_players_per_traitor", 5);
cfg.RegisterCVar("game.suspicion_starting_balance", 20);
}
public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false) public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false)
{ {
MinPlayers = _cfg.GetCVar<int>("game.suspicion_min_players"); MinPlayers = _cfg.GetCVar(CCVars.GameSuspicionMinPlayers);
MinTraitors = _cfg.GetCVar<int>("game.suspicion_min_traitors"); MinTraitors = _cfg.GetCVar(CCVars.GameSuspicionMinTraitors);
PlayersPerTraitor = _cfg.GetCVar<int>("game.suspicion_players_per_traitor"); PlayersPerTraitor = _cfg.GetCVar(CCVars.GameSuspicionPlayersPerTraitor);
TraitorStartingBalance = _cfg.GetCVar<int>("game.suspicion_starting_balance"); TraitorStartingBalance = _cfg.GetCVar(CCVars.GameSuspicionStartingBalance);
if (!force && readyPlayers.Count < MinPlayers) if (!force && readyPlayers.Count < MinPlayers)
{ {

View File

@@ -2,6 +2,7 @@
using System.Threading; using System.Threading;
using Content.Server.Interfaces.Chat; using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameTicking; using Content.Server.Interfaces.GameTicking;
using Content.Shared;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Server.Player; using Robust.Server.Player;
@@ -56,7 +57,7 @@ namespace Content.Server.GameTicking.GameRules
{ {
_checkTimerCancel = null; _checkTimerCancel = null;
if (!_cfg.GetCVar<bool>("game.enablewin")) if (!_cfg.GetCVar(CCVars.GameLobbyEnableWin))
return; return;
IPlayerSession winner = null; IPlayerSession winner = null;

View File

@@ -6,6 +6,7 @@ using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameTicking; using Content.Server.Interfaces.GameTicking;
using Content.Server.Mobs.Roles.Suspicion; using Content.Server.Mobs.Roles.Suspicion;
using Content.Server.Players; using Content.Server.Players;
using Content.Shared;
using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Damage;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
@@ -58,7 +59,7 @@ namespace Content.Server.GameTicking.GameRules
private void _checkWinConditions() private void _checkWinConditions()
{ {
if (!_cfg.GetCVar<bool>("game.enablewin")) if (!_cfg.GetCVar(CCVars.GameLobbyEnableWin))
return; return;
var traitorsAlive = 0; var traitorsAlive = 0;

View File

@@ -89,7 +89,7 @@ namespace Content.Server.GameTicking
[ViewVariables] private bool DisallowLateJoin { get; set; } = false; [ViewVariables] private bool DisallowLateJoin { get; set; } = false;
[ViewVariables] private bool LobbyEnabled => _configurationManager.GetCVar<bool>("game.lobbyenabled"); [ViewVariables] private bool LobbyEnabled => _configurationManager.GetCVar(CCVars.GameLobbyEnabled);
[ViewVariables] private bool _updateOnRoundEnd; [ViewVariables] private bool _updateOnRoundEnd;
private CancellationTokenSource _updateShutdownCts; private CancellationTokenSource _updateShutdownCts;
@@ -120,7 +120,7 @@ namespace Content.Server.GameTicking
public event Action<GameRuleAddedEventArgs> OnRuleAdded; public event Action<GameRuleAddedEventArgs> OnRuleAdded;
private TimeSpan LobbyDuration => private TimeSpan LobbyDuration =>
TimeSpan.FromSeconds(_configurationManager.GetCVar<int>("game.lobbyduration")); TimeSpan.FromSeconds(_configurationManager.GetCVar(CCVars.GameLobbyDuration));
public override void Initialize() public override void Initialize()
{ {
@@ -128,8 +128,6 @@ namespace Content.Server.GameTicking
DebugTools.Assert(!_initialized); DebugTools.Assert(!_initialized);
PresetSuspicion.RegisterCVars(_configurationManager);
_netManager.RegisterNetMessage<MsgTickerJoinLobby>(nameof(MsgTickerJoinLobby)); _netManager.RegisterNetMessage<MsgTickerJoinLobby>(nameof(MsgTickerJoinLobby));
_netManager.RegisterNetMessage<MsgTickerJoinGame>(nameof(MsgTickerJoinGame)); _netManager.RegisterNetMessage<MsgTickerJoinGame>(nameof(MsgTickerJoinGame));
_netManager.RegisterNetMessage<MsgTickerLobbyStatus>(nameof(MsgTickerLobbyStatus)); _netManager.RegisterNetMessage<MsgTickerLobbyStatus>(nameof(MsgTickerLobbyStatus));
@@ -287,9 +285,9 @@ namespace Content.Server.GameTicking
if (!preset.Start(assignedJobs.Keys.ToList(), force)) if (!preset.Start(assignedJobs.Keys.ToList(), force))
{ {
if (_configurationManager.GetCVar<bool>("game.fallbackenabled")) if (_configurationManager.GetCVar(CCVars.GameLobbyFallbackEnabled))
{ {
SetStartPreset(_configurationManager.GetCVar<string>("game.fallbackpreset")); SetStartPreset(_configurationManager.GetCVar(CCVars.GameLobbyFallbackPreset));
var newPreset = MakeGamePreset(profiles); var newPreset = MakeGamePreset(profiles);
_chatManager.DispatchServerAnnouncement( _chatManager.DispatchServerAnnouncement(
$"Failed to start {preset.ModeTitle} mode! Defaulting to {newPreset.ModeTitle}..."); $"Failed to start {preset.ModeTitle} mode! Defaulting to {newPreset.ModeTitle}...");

View File

@@ -25,7 +25,7 @@ namespace Content.Server.GlobalVerbs
return; return;
} }
if (!EntitySystem.Get<PointingSystem>().InRange(user.Transform.Coordinates, target.Transform.Coordinates)) if (!EntitySystem.Get<PointingSystem>().InRange(user, target.Transform.Coordinates))
{ {
return; return;
} }

View File

@@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.Interfaces; using Content.Server.Interfaces;
using Content.Server.Interfaces.Chat; using Content.Server.Interfaces.Chat;
using Content.Shared;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Newtonsoft.Json; using Newtonsoft.Json;
using Robust.Server.Interfaces.ServerStatus; using Robust.Server.Interfaces.ServerStatus;
@@ -27,9 +28,6 @@ namespace Content.Server
void IPostInjectInit.PostInject() void IPostInjectInit.PostInject()
{ {
_configurationManager.RegisterCVar<string>("status.mommiurl", null);
_configurationManager.RegisterCVar<string>("status.mommipassword", null);
_statusHost.AddHandler(_handleChatPost); _statusHost.AddHandler(_handleChatPost);
} }
@@ -46,8 +44,8 @@ namespace Content.Server
private async Task _sendMessageInternal(string type, object messageObject) private async Task _sendMessageInternal(string type, object messageObject)
{ {
var url = _configurationManager.GetCVar<string>("status.mommiurl"); var url = _configurationManager.GetCVar(CCVars.StatusMoMMIUrl);
var password = _configurationManager.GetCVar<string>("status.mommipassword"); var password = _configurationManager.GetCVar(CCVars.StatusMoMMIPassword);
if (string.IsNullOrWhiteSpace(url)) if (string.IsNullOrWhiteSpace(url))
{ {
return; return;
@@ -83,7 +81,7 @@ namespace Content.Server
return false; return false;
} }
var password = _configurationManager.GetCVar<string>("status.mommipassword"); var password = _configurationManager.GetCVar(CCVars.StatusMoMMIPassword);
OOCPostMessage message = null; OOCPostMessage message = null;
try try

Some files were not shown because too many files have changed in this diff Show More