Merge branch 'master' into 2020-04-28-tool-component

# Conflicts:
#	Content.Server/GameObjects/Components/AnchorableComponent.cs
#	Content.Server/GameObjects/Components/Gravity/GravityGeneratorComponent.cs
#	Content.Server/GameObjects/Components/Interactable/Tools/CrowbarComponent.cs
#	Content.Server/GameObjects/Components/Power/PowerTransferComponent.cs
#	Content.Server/GameObjects/Components/WiresComponent.cs
#	Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs
#	Resources/Prototypes/Entities/Items/tools.yml
This commit is contained in:
zumorica
2020-05-23 12:02:34 +02:00
305 changed files with 33490 additions and 982 deletions

View File

@@ -4,6 +4,7 @@ using Content.Shared.GameObjects.Components.Markers;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
@@ -48,8 +49,7 @@ namespace Content.Client.Commands
public bool Execute(IDebugConsole console, params string[] args)
{
IoCManager.Resolve<IEntitySystemManager>()
.GetEntitySystem<SubFloorHideSystem>()
EntitySystem.Get<SubFloorHideSystem>()
.EnableAll ^= true;
return false;

View File

@@ -148,6 +148,14 @@ namespace Content.Client
"RandomPottedPlant",
"CommunicationsConsole",
"BarSign",
"DroppedBodyPart",
"DroppedMechanism",
"BodyManager",
"Stunnable",
"SolarPanel",
"BodyScanner",
"Stunbaton",
"EmergencyClosetFill"
};
foreach (var ignoreName in registerIgnore)
@@ -239,7 +247,6 @@ namespace Content.Client
IoCManager.Resolve<IChatManager>().Initialize();
IoCManager.Resolve<ISandboxManager>().Initialize();
IoCManager.Resolve<IClientPreferencesManager>().Initialize();
IoCManager.Resolve<IItemSlotManager>().Initialize();
_baseClient.RunLevelChanged += (sender, args) =>
{

View File

@@ -30,7 +30,7 @@ namespace Content.Client.GameObjects.Components.Chemistry
//Handle net updates
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
var cast = (InjectorComponentState) curState;
var cast = (InjectorComponentState) curState;
if (cast != null)
{
CurrentVolume = cast.CurrentVolume;
@@ -64,7 +64,7 @@ namespace Content.Client.GameObjects.Components.Chemistry
{
return;
}
_parent._uiUpdateNeeded = false;
//Update current volume and injector state

View File

@@ -0,0 +1,45 @@
using System;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Client.GameObjects.Components.Animations;
using Robust.Shared.Animations;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components
{
[RegisterComponent]
public class EmergencyLightComponent : Component
{
public override string Name => "EmergencyLight";
protected override void Startup()
{
base.Startup();
var animation = new Animation
{
Length = TimeSpan.FromSeconds(4),
AnimationTracks =
{
new AnimationTrackComponentProperty
{
ComponentType = typeof(PointLightComponent),
InterpolationMode = AnimationInterpolationMode.Linear,
Property = nameof(PointLightComponent.Rotation),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(Angle.Zero, 0),
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(1080), 4)
}
}
}
};
var playerComponent = Owner.EnsureComponent<AnimationPlayerComponent>();
playerComponent.Play(animation, "emergency");
playerComponent.AnimationCompleted += s => playerComponent.Play(animation, s);
}
}
}

View File

@@ -1,17 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Shared.GameObjects.Components.Instruments;
using JetBrains.Annotations;
using NFluidsynth;
using Robust.Shared.GameObjects;
using Robust.Client.Audio.Midi;
using Robust.Shared.Audio.Midi;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using Logger = Robust.Shared.Log.Logger;
using MidiEvent = Robust.Shared.Audio.Midi.MidiEvent;
using Timer = Robust.Shared.Timers.Timer;
@@ -20,6 +22,8 @@ namespace Content.Client.GameObjects.Components.Instruments
[RegisterComponent]
public class InstrumentComponent : SharedInstrumentComponent
{
public const float TimeBetweenNetMessages = 1.0f;
/// <summary>
/// Called when a midi song stops playing.
/// </summary>
@@ -27,17 +31,22 @@ namespace Content.Client.GameObjects.Components.Instruments
#pragma warning disable 649
[Dependency] private IMidiManager _midiManager;
[Dependency] private readonly IGameTiming _timing;
[Dependency] private readonly IGameTiming _gameTiming;
#pragma warning restore 649
[CanBeNull]
private IMidiRenderer _renderer;
private int _instrumentProgram = 1;
private byte _instrumentProgram = 1;
private uint _syncSequencerTick;
/// <summary>
/// A queue of MidiEvents to be sent to the server.
/// </summary>
private Queue<MidiEvent> _eventQueue = new Queue<MidiEvent>();
[ViewVariables]
private readonly Queue<MidiEvent> _midiQueue = new Queue<MidiEvent>();
[ViewVariables]
private float _timer = 0f;
/// <summary>
/// Whether a midi song will loop or not.
@@ -59,7 +68,7 @@ namespace Content.Client.GameObjects.Components.Instruments
/// Changes the instrument the midi renderer will play.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public int InstrumentProgram
public byte InstrumentProgram
{
get => _instrumentProgram;
set
@@ -84,61 +93,102 @@ namespace Content.Client.GameObjects.Components.Instruments
[ViewVariables]
public bool IsInputOpen => _renderer?.Status == MidiRendererStatus.Input;
/// <summary>
/// Whether the midi renderer is alive or not.
/// </summary>
[ViewVariables]
public bool IsRendererAlive => _renderer != null;
public override void Initialize()
{
base.Initialize();
IoCManager.InjectDependencies(this);
}
protected void SetupRenderer()
{
if (IsRendererAlive)
return;
_renderer = _midiManager.GetNewRenderer();
if (_renderer != null)
{
_renderer.MidiProgram = _instrumentProgram;
_renderer.TrackingEntity = Owner;
_renderer.OnMidiPlayerFinished += () => { OnMidiPlaybackEnded?.Invoke(); };
_renderer.OnMidiPlayerFinished += () => { OnMidiPlaybackEnded?.Invoke(); EndRenderer(); SendNetworkMessage(new InstrumentStopMidiMessage()); };
}
}
protected void EndRenderer()
{
if (IsInputOpen)
CloseInput();
if (IsMidiOpen)
CloseMidi();
_renderer?.StopAllNotes();
var renderer = _renderer;
// We dispose of the synth two seconds from now to allow the last notes to stop from playing.
Timer.Spawn(2000, () => { renderer?.Dispose(); });
_renderer = null;
_midiQueue.Clear();
}
protected override void Shutdown()
{
base.Shutdown();
_renderer?.Dispose();
EndRenderer();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _instrumentProgram, "program", 1);
serializer.DataField(ref _instrumentProgram, "program", (byte)1);
}
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null)
{
base.HandleNetworkMessage(message, channel, session);
if (_renderer == null)
{
return;
}
switch (message)
{
case InstrumentMidiEventMessage midiEventMessage:
// If we're the ones sending the MidiEvents, we ignore this message.
if (IsInputOpen || IsMidiOpen) break;
Timer.Spawn((int) (500 + _timing.CurTime.TotalMilliseconds - midiEventMessage.Timestamp),
() => _renderer.SendMidiEvent(midiEventMessage.MidiEvent));
break;
case InstrumentStopMidiMessage _:
_renderer.StopAllNotes();
if (IsInputOpen) CloseInput();
if (IsMidiOpen) CloseMidi();
if (!IsRendererAlive || IsInputOpen || IsMidiOpen) break;
for (var i = 0; i < midiEventMessage.MidiEvent.Length; i++)
{
var ev = midiEventMessage.MidiEvent[i];
var delta = ((uint)TimeBetweenNetMessages*1250) + ev.Timestamp - _syncSequencerTick;
_renderer?.ScheduleMidiEvent(ev, delta, true);
}
break;
}
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is InstrumentState state)) return;
if (state.Playing)
{
SetupRenderer();
_syncSequencerTick = state.SequencerTick;
}
else
EndRenderer();
}
/// <inheritdoc cref="MidiRenderer.OpenInput"/>
public bool OpenInput()
{
SetupRenderer();
SendNetworkMessage(new InstrumentStartMidiMessage());
if (_renderer != null && _renderer.OpenInput())
{
_renderer.OnMidiEvent += RendererOnMidiEvent;
@@ -156,13 +206,17 @@ namespace Content.Client.GameObjects.Components.Instruments
return false;
}
_renderer.OnMidiEvent -= RendererOnMidiEvent;
EndRenderer();
SendNetworkMessage(new InstrumentStopMidiMessage());
return true;
}
/// <inheritdoc cref="MidiRenderer.OpenMidi(string)"/>
public bool OpenMidi(string filename)
{
SetupRenderer();
SendNetworkMessage(new InstrumentStartMidiMessage());
if (_renderer == null || !_renderer.OpenMidi(filename))
{
return false;
@@ -180,7 +234,8 @@ namespace Content.Client.GameObjects.Components.Instruments
return false;
}
_renderer.OnMidiEvent -= RendererOnMidiEvent;
EndRenderer();
SendNetworkMessage(new InstrumentStopMidiMessage());
return true;
}
@@ -190,7 +245,29 @@ namespace Content.Client.GameObjects.Components.Instruments
/// <param name="midiEvent">The received midi event</param>
private void RendererOnMidiEvent(MidiEvent midiEvent)
{
SendNetworkMessage(new InstrumentMidiEventMessage(midiEvent, _timing.CurTime.TotalMilliseconds));
_midiQueue.Enqueue(midiEvent);
}
public override void Update(float delta)
{
if (!IsMidiOpen && !IsInputOpen)
return;
_timer -= delta;
if (_timer > 0f) return;
SendAllMidiMessages();
_timer = TimeBetweenNetMessages;
}
private void SendAllMidiMessages()
{
if (_midiQueue.Count == 0) return;
var events = _midiQueue.ToArray();
_midiQueue.Clear();
SendNetworkMessage(new InstrumentMidiEventMessage(events));
}
}
}

View File

@@ -137,10 +137,11 @@ namespace Content.Client.GameObjects.Components.Kitchen
{
Text = (index <= 0 ? 1 : index).ToString(),
TextAlign = Label.AlignMode.Center,
ToggleMode = true,
Group = CookTimeButtonGroup,
};
CookTimeButtonVbox.AddChild(newButton);
newButton.OnPressed += args =>
newButton.OnToggled += args =>
{
OnCookTimeSelected?.Invoke(args);
_cookTimeInfoLabel.Text = $"{Loc.GetString("COOK TIME")}: {VisualCookTime}";

View File

@@ -1,6 +1,5 @@
using System;
using Content.Shared.GameObjects.Components.Mobs;
using Microsoft.CodeAnalysis.Completion;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Client.GameObjects.Components.Animations;

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Sound;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Random;
@@ -9,6 +10,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Timers;
using Robust.Shared.Utility;
namespace Content.Client.GameObjects.Components.Sound
{
@@ -54,7 +56,7 @@ namespace Content.Client.GameObjects.Components.Sound
Timer.Spawn((int) schedule.Delay + (_random.Next((int) schedule.RandomDelay)),() =>
{
if (!schedule.Play) return; // We make sure this hasn't changed.
if (_audioSystem == null) _audioSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
if (_audioSystem == null) _audioSystem = EntitySystem.Get<AudioSystem>();
_audioStreams.Add(schedule,_audioSystem.Play(schedule.Filename, Owner, schedule.AudioParams));
if (schedule.Times == 0) return;
@@ -87,7 +89,7 @@ namespace Content.Client.GameObjects.Components.Sound
public override void Initialize()
{
base.Initialize();
IoCManager.Resolve<IEntitySystemManager>().TryGetEntitySystem(out _audioSystem);
EntitySystem.TryGet(out _audioSystem);
}
public override void ExposeData(ObjectSerializer serializer)

View File

@@ -1,6 +1,7 @@
using Content.Shared.GameObjects.Components.Storage;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
@@ -8,6 +9,7 @@ namespace Content.Client.GameObjects.Components.Storage
{
public sealed class StorageVisualizer2D : AppearanceVisualizer
{
private string _stateBase;
private string _stateOpen;
private string _stateClosed;
@@ -15,7 +17,12 @@ namespace Content.Client.GameObjects.Components.Storage
{
base.LoadData(node);
if (node.TryGetNode("state_open", out var child))
if (node.TryGetNode("state", out var child))
{
_stateBase = child.AsString();
}
if (node.TryGetNode("state_open", out child))
{
_stateOpen = child.AsString();
}
@@ -26,6 +33,19 @@ namespace Content.Client.GameObjects.Components.Storage
}
}
public override void InitializeEntity(IEntity entity)
{
if (!entity.TryGetComponent(out ISpriteComponent sprite))
{
return;
}
if (_stateBase != null)
{
sprite.LayerSetState(0, _stateBase);
}
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
@@ -36,7 +56,9 @@ namespace Content.Client.GameObjects.Components.Storage
}
component.TryGetData(StorageVisuals.Open, out bool open);
sprite.LayerSetState(StorageVisualLayers.Door, open ? _stateOpen : _stateClosed);
sprite.LayerSetState(StorageVisualLayers.Door, open
? _stateOpen ?? $"{_stateBase}_open"
: _stateClosed ?? $"{_stateBase}_door");
}
}

View File

@@ -0,0 +1,25 @@
using Content.Client.GameObjects.Components.Instruments;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Client.GameObjects.EntitySystems
{
public class InstrumentSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(InstrumentComponent));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var entity in RelevantEntities)
{
entity.GetComponent<InstrumentComponent>().Update(frameTime);
}
}
}
}

View File

@@ -1,25 +1,37 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using Content.Client.State;
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.EntitySystemMessages;
using Content.Shared.Input;
using JetBrains.Annotations;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.Utility;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
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.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Timer = Robust.Shared.Timers.Timer;
namespace Content.Client.GameObjects.EntitySystems
{
@@ -31,11 +43,21 @@ namespace Content.Client.GameObjects.EntitySystems
[Dependency] private readonly IEntityManager _entityManager;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IInputManager _inputManager;
[Dependency] private readonly IItemSlotManager _itemSlotManager;
[Dependency] private readonly IGameTiming _gameTiming;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
[Dependency] private readonly IResourceCache _resourceCache;
#pragma warning restore 649
private VerbPopup _currentPopup;
private EntityList _currentEntityList;
private VerbPopup _currentVerbListRoot;
private VerbPopup _currentGroupList;
private EntityUid _currentEntity;
private bool IsAnyContextMenuOpen => _currentEntityList != null || _currentVerbListRoot != null;
public override void Initialize()
{
base.Initialize();
@@ -51,29 +73,28 @@ namespace Content.Client.GameObjects.EntitySystems
public void OpenContextMenu(IEntity entity, ScreenCoordinates screenCoordinates)
{
if (_currentPopup != null)
if (_currentVerbListRoot != null)
{
CloseContextMenu();
CloseVerbMenu();
}
_currentEntity = entity.Uid;
_currentPopup = new VerbPopup();
_currentPopup.UserInterfaceManager.ModalRoot.AddChild(_currentPopup);
_currentPopup.OnPopupHide += CloseContextMenu;
_currentVerbListRoot = new VerbPopup();
_userInterfaceManager.ModalRoot.AddChild(_currentVerbListRoot);
_currentVerbListRoot.OnPopupHide += CloseVerbMenu;
_currentPopup.List.AddChild(new Label {Text = "Waiting on Server..."});
_currentVerbListRoot.List.AddChild(new Label {Text = "Waiting on Server..."});
RaiseNetworkEvent(new VerbSystemMessages.RequestVerbsMessage(_currentEntity));
var size = _currentPopup.List.CombinedMinimumSize;
var box = UIBox2.FromDimensions(screenCoordinates.Position, size);
_currentPopup.Open(box);
var box = UIBox2.FromDimensions(screenCoordinates.Position, (1, 1));
_currentVerbListRoot.Open(box);
}
private bool OnOpenContextMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
{
if (_currentPopup != null)
if (IsAnyContextMenuOpen)
{
CloseContextMenu();
CloseAllMenus();
return true;
}
@@ -89,20 +110,29 @@ namespace Content.Client.GameObjects.EntitySystems
return false;
}
_currentPopup = new VerbPopup();
_currentPopup.OnPopupHide += CloseContextMenu;
foreach (var entity in entities)
_currentEntityList = new EntityList();
_currentEntityList.OnPopupHide += CloseAllMenus;
for (var i = 0; i < entities.Count; i++)
{
var button = new Button {Text = entity.Name};
_currentPopup.List.AddChild(button);
button.OnPressed += _ => OnContextButtonPressed(entity);
if (i != 0)
{
_currentEntityList.List.AddChild(new PanelContainer
{
CustomMinimumSize = (0, 2),
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#333")}
});
}
var entity = entities[i];
_currentEntityList.List.AddChild(new EntityButton(this, entity));
}
_currentPopup.UserInterfaceManager.ModalRoot.AddChild(_currentPopup);
_userInterfaceManager.ModalRoot.AddChild(_currentEntityList);
var size = _currentPopup.List.CombinedMinimumSize;
var size = _currentEntityList.List.CombinedMinimumSize;
var box = UIBox2.FromDimensions(args.ScreenCoordinates.Position, size);
_currentPopup.Open(box);
_currentEntityList.Open(box);
return true;
}
@@ -119,28 +149,31 @@ namespace Content.Client.GameObjects.EntitySystems
return;
}
DebugTools.AssertNotNull(_currentPopup);
DebugTools.AssertNotNull(_currentVerbListRoot);
var buttons = new Dictionary<string, List<Button>>();
var buttons = new Dictionary<string, List<ListedVerbData>>();
var groupIcons = new Dictionary<string, SpriteSpecifier>();
var vBox = _currentPopup.List;
var vBox = _currentVerbListRoot.List;
vBox.DisposeAllChildren();
// Local variable so that scope capture ensures this is the correct value.
var curEntity = _currentEntity;
foreach (var data in msg.Verbs)
{
var button = new Button {Text = data.Text, Disabled = !data.Available};
if (data.Available)
var list = buttons.GetOrNew(data.Category);
if (data.CategoryIcon != null && !groupIcons.ContainsKey(data.Category))
{
button.OnPressed += _ =>
{
RaiseNetworkEvent(new VerbSystemMessages.UseVerbMessage(_currentEntity, data.Key));
CloseContextMenu();
};
groupIcons.Add(data.Category, data.CategoryIcon);
}
if(!buttons.ContainsKey(data.Category))
buttons[data.Category] = new List<Button>();
buttons[data.Category].Add(button);
list.Add(new ListedVerbData(data.Text, !data.Available, data.Key, entity.ToString(), () =>
{
RaiseNetworkEvent(new VerbSystemMessages.UseVerbMessage(curEntity, data.Key));
CloseAllMenus();
}, data.Icon));
}
var user = GetUserEntity();
@@ -150,55 +183,87 @@ namespace Content.Client.GameObjects.EntitySystems
if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity))
continue;
if (VerbUtility.IsVerbInvisible(verb, user, component, out var vis))
var verbData = verb.GetData(user, component);
if (verbData.IsInvisible)
continue;
var disabled = vis != VerbVisibility.Visible;
var category = verb.GetCategory(user, component);
var list = buttons.GetOrNew(verbData.Category);
if (verbData.CategoryIcon != null && !groupIcons.ContainsKey(verbData.Category))
{
groupIcons.Add(verbData.Category, verbData.CategoryIcon);
}
if(!buttons.ContainsKey(category))
buttons[category] = new List<Button>();
buttons[category].Add(CreateVerbButton(verb.GetText(user, component), disabled, verb.ToString(),
entity.ToString(), () => verb.Activate(user, component)));
list.Add(new ListedVerbData(verbData.Text, verbData.IsDisabled, verb.ToString(), entity.ToString(),
() => verb.Activate(user, component), verbData.Icon));
}
//Get global verbs. Visible for all entities regardless of their components.
foreach (var globalVerb in VerbUtility.GetGlobalVerbs(Assembly.GetExecutingAssembly()))
{
if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity))
continue;
if (VerbUtility.IsVerbInvisible(globalVerb, user, entity, out var vis))
var verbData = globalVerb.GetData(user, entity);
if (verbData.IsInvisible)
continue;
var disabled = vis != VerbVisibility.Visible;
var category = globalVerb.GetCategory(user, entity);
var list = buttons.GetOrNew(verbData.Category);
if(!buttons.ContainsKey(category))
buttons[category] = new List<Button>();
if (verbData.CategoryIcon != null && !groupIcons.ContainsKey(verbData.Category))
{
groupIcons.Add(verbData.Category, verbData.CategoryIcon);
}
buttons[category].Add(CreateVerbButton(globalVerb.GetText(user, entity), disabled, globalVerb.ToString(),
entity.ToString(), () => globalVerb.Activate(user, entity)));
list.Add(new ListedVerbData(verbData.Text, verbData.IsDisabled, globalVerb.ToString(),
entity.ToString(),
() => globalVerb.Activate(user, entity), verbData.Icon));
}
if (buttons.Count > 0)
{
var first = true;
foreach (var (category, verbs) in buttons)
{
if (string.IsNullOrEmpty(category))
continue;
vBox.AddChild(CreateCategoryButton(category, verbs));
if (!first)
{
vBox.AddChild(new PanelContainer
{
CustomMinimumSize = (0, 2),
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#333")}
});
}
first = false;
groupIcons.TryGetValue(category, out var icon);
vBox.AddChild(CreateCategoryButton(category, verbs, icon));
}
if (buttons.ContainsKey(""))
{
buttons[""].Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.Ordinal));
buttons[""].Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.CurrentCulture));
foreach (var verb in buttons[""])
{
vBox.AddChild(verb);
if (!first)
{
vBox.AddChild(new PanelContainer
{
CustomMinimumSize = (0, 2),
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#333")}
});
}
first = false;
vBox.AddChild(CreateVerbButton(verb));
}
}
}
@@ -210,69 +275,364 @@ namespace Content.Client.GameObjects.EntitySystems
}
}
private Button CreateVerbButton(string text, bool disabled, string verbName, string ownerName, Action action)
private VerbButton CreateVerbButton(ListedVerbData data)
{
var button = new Button
var button = new VerbButton
{
Text = text,
Disabled = disabled
Text = data.Text,
Disabled = data.Disabled
};
if (!disabled)
if (data.Icon != null)
{
button.Icon = data.Icon.Frame0();
}
if (!data.Disabled)
{
button.OnPressed += _ =>
{
CloseContextMenu();
CloseAllMenus();
try
{
action.Invoke();
data.Action.Invoke();
}
catch (Exception e)
{
Logger.ErrorS("verb", "Exception in verb {0} on {1}:\n{2}", verbName, ownerName, e);
Logger.ErrorS("verb", "Exception in verb {0} on {1}:\n{2}", data.VerbName, data.OwnerName, e);
}
};
}
return button;
}
private Button CreateCategoryButton(string text, List<Button> verbButtons)
private Control CreateCategoryButton(string text, List<ListedVerbData> verbButtons, SpriteSpecifier icon)
{
verbButtons.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.Ordinal));
verbButtons.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.CurrentCulture));
var button = new Button
return new VerbGroupButton(this, verbButtons, icon)
{
Text = $"{text}...",
Text = text,
};
button.OnPressed += _ =>
{
_currentPopup.List.DisposeAllChildren();
foreach (var verb in verbButtons)
{
_currentPopup.List.AddChild(verb);
}
};
return button;
}
private void CloseContextMenu()
private void CloseVerbMenu()
{
_currentPopup?.Dispose();
_currentPopup = null;
_currentVerbListRoot?.Dispose();
_currentVerbListRoot = null;
_currentEntity = EntityUid.Invalid;
}
private void CloseEntityList()
{
_currentEntityList?.Dispose();
_currentEntityList = null;
}
private void CloseAllMenus()
{
CloseVerbMenu();
CloseEntityList();
CloseGroupMenu();
}
private void CloseGroupMenu()
{
_currentGroupList?.Dispose();
_currentGroupList = null;
}
private IEntity GetUserEntity()
{
return _playerManager.LocalPlayer.ControlledEntity;
}
private sealed class EntityList : Popup
{
public VBoxContainer List { get; }
public EntityList()
{
AddChild(new PanelContainer
{
Children = {(List = new VBoxContainer())},
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#111E")}
});
}
}
private sealed class VerbPopup : Popup
{
public VBoxContainer List { get; }
public VerbPopup()
{
AddChild(List = new VBoxContainer());
AddChild(new PanelContainer
{
Children = {(List = new VBoxContainer())},
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#111E")}
});
}
}
private sealed class EntityButton : Control
{
private readonly VerbSystem _master;
private readonly IEntity _entity;
public EntityButton(VerbSystem master, IEntity entity)
{
_master = master;
_entity = entity;
MouseFilter = MouseFilterMode.Stop;
var control = new HBoxContainer {SeparationOverride = 6};
if (entity.TryGetComponent(out ISpriteComponent sprite))
{
control.AddChild(new SpriteView {Sprite = sprite});
}
control.AddChild(new MarginContainer
{
MarginLeftOverride = 4,
MarginRightOverride = 4,
Children = {new Label {Text = entity.Name}}
});
AddChild(control);
}
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
{
base.KeyBindDown(args);
if (args.Function == ContentKeyFunctions.OpenContextMenu)
{
_master.OnContextButtonPressed(_entity);
return;
}
if (args.Function == EngineKeyFunctions.Use)
{
var inputSys = _master.EntitySystemManager.GetEntitySystem<InputSystem>();
var func = args.Function;
var funcId = _master._inputManager.NetworkBindMap.KeyFunctionID(args.Function);
var message = new FullInputCmdMessage(_master._gameTiming.CurTick, funcId, BoundKeyState.Down,
_entity.Transform.GridPosition,
args.PointerLocation, _entity.Uid);
// client side command handlers will always be sent the local player session.
var session = _master._playerManager.LocalPlayer.Session;
inputSys.HandleInputCommand(session, func, message);
_master.CloseAllMenus();
return;
}
if (_master._itemSlotManager.OnButtonPressed(args, _entity))
{
_master.CloseAllMenus();
}
}
protected override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
if (UserInterfaceManager.CurrentlyHovered == this)
{
handle.DrawRect(PixelSizeBox, Color.DarkSlateGray);
}
}
}
private sealed class VerbButton : BaseButton
{
private readonly Label _label;
private readonly TextureRect _icon;
public Texture Icon
{
get => _icon.Texture;
set => _icon.Texture = value;
}
public string Text
{
get => _label.Text;
set => _label.Text = value;
}
public VerbButton()
{
AddChild(new HBoxContainer
{
Children =
{
(_icon = new TextureRect
{
CustomMinimumSize = (32, 32),
Stretch = TextureRect.StretchMode.KeepCentered
}),
(_label = new Label()),
// Padding
new Control {CustomMinimumSize = (8, 0)}
}
});
}
protected override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
if (DrawMode == DrawModeEnum.Hover)
{
handle.DrawRect(PixelSizeBox, Color.DarkSlateGray);
}
}
}
private sealed class VerbGroupButton : Control
{
private static readonly TimeSpan HoverDelay = TimeSpan.FromSeconds(0.2);
private readonly VerbSystem _system;
public List<ListedVerbData> VerbButtons { get; }
private readonly Label _label;
private readonly TextureRect _icon;
private CancellationTokenSource _openCancel;
public string Text
{
get => _label.Text;
set => _label.Text = value;
}
public Texture Icon
{
get => _icon.Texture;
set => _icon.Texture = value;
}
public VerbGroupButton(VerbSystem system, List<ListedVerbData> verbButtons, SpriteSpecifier icon)
{
_system = system;
VerbButtons = verbButtons;
MouseFilter = MouseFilterMode.Stop;
AddChild(new HBoxContainer
{
Children =
{
(_icon = new TextureRect
{
CustomMinimumSize = (32, 32),
Stretch = TextureRect.StretchMode.KeepCentered
}),
(_label = new Label
{
SizeFlagsHorizontal = SizeFlags.FillExpand
}),
// Padding
new Control {CustomMinimumSize = (8, 0)},
new TextureRect
{
Texture = IoCManager.Resolve<IResourceCache>()
.GetTexture("/Textures/UserInterface/VerbIcons/group.svg.96dpi.png"),
Stretch = TextureRect.StretchMode.KeepCentered,
}
}
});
if (icon != null)
{
_icon.Texture = icon.Frame0();
}
}
protected override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
if (this == UserInterfaceManager.CurrentlyHovered)
{
handle.DrawRect(PixelSizeBox, Color.DarkSlateGray);
}
}
protected override void MouseEntered()
{
base.MouseEntered();
_openCancel = new CancellationTokenSource();
Timer.Spawn(HoverDelay, () =>
{
if (_system._currentGroupList != null)
{
_system.CloseGroupMenu();
}
var popup = _system._currentGroupList = new VerbPopup();
var first = true;
foreach (var verb in VerbButtons)
{
if (!first)
{
popup.List.AddChild(new PanelContainer
{
CustomMinimumSize = (0, 2),
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#333")}
});
}
first = false;
popup.List.AddChild(_system.CreateVerbButton(verb));
}
UserInterfaceManager.ModalRoot.AddChild(popup);
popup.Open(UIBox2.FromDimensions(GlobalPosition + (Width, 0), (1, 1)), GlobalPosition);
}, _openCancel.Token);
}
protected override void MouseExited()
{
base.MouseExited();
_openCancel?.Cancel();
_openCancel = null;
}
}
private sealed class ListedVerbData
{
public string Text { get; }
public bool Disabled { get; }
public string VerbName { get; }
public string OwnerName { get; }
public SpriteSpecifier Icon { get; }
public Action Action { get; }
public ListedVerbData(string text, bool disabled, string verbName, string ownerName,
Action action, SpriteSpecifier icon)
{
Text = text;
Disabled = disabled;
VerbName = verbName;
OwnerName = ownerName;
Action = action;
Icon = icon;
}
}
}

View File

@@ -12,17 +12,19 @@ namespace Content.Client.GlobalVerbs
[GlobalVerb]
class ViewVariablesVerb : GlobalVerb
{
public override string GetText(IEntity user, IEntity target) => "View variables";
public override string GetCategory(IEntity user, IEntity target) => "Debug";
public override bool RequireInteractionRange => false;
public override VerbVisibility GetVisibility(IEntity user, IEntity target)
public override void GetData(IEntity user, IEntity target, VerbData data)
{
var groupController = IoCManager.Resolve<IClientConGroupController>();
if (groupController.CanViewVar())
return VerbVisibility.Visible;
return VerbVisibility.Invisible;
if (!groupController.CanViewVar())
{
data.Visibility = VerbVisibility.Invisible;
return;
}
data.Text = "View Variables";
data.CategoryData = VerbCategories.Debug;
}
public override void Activate(IEntity user, IEntity target)

View File

@@ -0,0 +1,33 @@
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.Graphics;
using Robust.Shared.Maths;
using System;
namespace Robust.Client.UserInterface.Controls
{
public class CooldownGraphic : Control
{
public float Fraction { get; set; }
protected override void Draw(DrawingHandleScreen handle)
{
const int maxSegments = 64;
const float segment = MathHelper.TwoPi / maxSegments;
var segments = (int)Math.Max(2, Math.Ceiling(maxSegments * Fraction)); // ensure that we always have 3 vertices
var max = MathHelper.TwoPi * Fraction;
var radius = (Math.Min(SizeBox.Height, SizeBox.Width) / 2) * 0.875f; // 28/32 = 0.875 - 2 pixels inwards from the edge
Span<Vector2> vertices = stackalloc Vector2[segments + 1];
vertices[0] = PixelPosition + SizeBox.Center;
for (int i = 0; i < segments; i++)
{
var angle = MathHelper.Pi + Math.Min(max, segment * i);
vertices[i + 1] = vertices[0] + new Vector2((float) Math.Sin(angle) * radius, (float) Math.Cos(angle) * radius);
}
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, vertices, new Color(0.3f, 0.3f, 0.4f, 0.5f));
}
}
}

View File

@@ -6,7 +6,6 @@ namespace Content.Client.UserInterface
{
public interface IItemSlotManager
{
void Initialize();
bool OnButtonPressed(GUIBoundKeyEventArgs args, IEntity item);
void UpdateCooldown(ItemSlotButton cooldownTexture, IEntity entity);
bool SetItemSlot(ItemSlotButton button, IEntity entity);

View File

@@ -1,4 +1,5 @@
using System;
using Content.Client.UserInterface;
using Content.Shared.Input;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
@@ -13,7 +14,7 @@ namespace Content.Client.GameObjects
public TextureRect Button { get; }
public SpriteView SpriteView { get; }
public BaseButton StorageButton { get; }
public TextureRect CooldownCircle { get; }
public CooldownGraphic CooldownDisplay { get; }
public Action<GUIBoundKeyEventArgs> OnPressed { get; set; }
public Action<GUIBoundKeyEventArgs> OnStoragePressed { get; set; }
@@ -56,12 +57,10 @@ namespace Content.Client.GameObjects
StorageButton.OnPressed += OnStorageButtonPressed;
AddChild(CooldownCircle = new TextureRect
AddChild(CooldownDisplay = new CooldownGraphic
{
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
SizeFlagsVertical = SizeFlags.ShrinkCenter,
Stretch = TextureRect.StretchMode.KeepCentered,
TextureScale = (2, 2),
SizeFlagsHorizontal = SizeFlags.Fill,
SizeFlagsVertical = SizeFlags.Fill,
Visible = false,
});
}

View File

@@ -30,22 +30,8 @@ namespace Content.Client.UserInterface
[Dependency] private readonly IInputManager _inputManager;
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
[Dependency] private readonly IEyeManager _eyeManager;
[Dependency] private readonly IResourceCache _resourceCache;
#pragma warning restore 0649
private const int CooldownLevels = 8;
private readonly Texture[] _texturesCooldownOverlay = new Texture[CooldownLevels];
public void Initialize()
{
for (var i = 0; i < CooldownLevels; i++)
{
_texturesCooldownOverlay[i] =
_resourceCache.GetTexture($"/Textures/UserInterface/Inventory/cooldown-{i}.png");
}
}
public bool SetItemSlot(ItemSlotButton button, IEntity entity)
{
if (entity == null)
@@ -103,7 +89,7 @@ namespace Content.Client.UserInterface
public void UpdateCooldown(ItemSlotButton button, IEntity entity)
{
var cooldownTexture = button.CooldownCircle;
var cooldownDisplay = button.CooldownDisplay;
if (entity != null
&& entity.TryGetComponent(out ItemCooldownComponent cooldown)
@@ -115,30 +101,23 @@ namespace Content.Client.UserInterface
var length = (end - start).TotalSeconds;
var progress = (_gameTiming.CurTime - start).TotalSeconds;
var ratio = (float)(progress / length);
var ratio = 1 - (float)(progress / length).Clamp(0, 1);
var textureIndex = CalculateCooldownLevel(ratio);
if (textureIndex == CooldownLevels)
cooldownDisplay.Fraction = ratio;
if (ratio > 0)
{
cooldownTexture.Visible = false;
cooldownDisplay.Visible = true;
}
else
{
cooldownTexture.Visible = true;
cooldownTexture.Texture = _texturesCooldownOverlay[textureIndex];
cooldownDisplay.Visible = false;
}
}
else
{
cooldownTexture.Visible = false;
cooldownDisplay.Visible = false;
}
}
internal static int CalculateCooldownLevel(float cooldownValue)
{
var val = cooldownValue.Clamp(0, 1);
val *= CooldownLevels;
return (int)Math.Floor(val);
}
}
}

View File

@@ -40,7 +40,7 @@ namespace Content.IntegrationTests.Tests
uniform = entityMan.SpawnEntity("JanitorUniform", MapCoordinates.Nullspace);
idCard = entityMan.SpawnEntity("AssistantIDCard", MapCoordinates.Nullspace);
pocketItem = entityMan.SpawnEntity("FlashlightLantern", MapCoordinates.Nullspace);
var tooBigItem = entityMan.SpawnEntity("RedToolboxItem", MapCoordinates.Nullspace);
var tooBigItem = entityMan.SpawnEntity("ToolboxEmergency", MapCoordinates.Nullspace);
inventory = human.GetComponent<InventoryComponent>();

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Interfaces.GameObjects.Components.Movement;
using Content.Shared.Physics;
using Robust.Server.AI;
@@ -114,7 +115,7 @@ namespace Content.Server.AI
var ray = new CollisionRay(myTransform.WorldPosition, dir.Normalized, (int)(CollisionGroup.MobImpassable | CollisionGroup.Impassable));
// cast the ray
var result = _physMan.IntersectRay(myTransform.MapID, ray, maxRayLen, SelfEntity);
var result = _physMan.IntersectRay(myTransform.MapID, ray, maxRayLen, SelfEntity).First();
// add to visible list
if (result.HitEntity == entity)

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Movement;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces.Chat;
@@ -127,17 +128,20 @@ namespace Content.Server.AI
{
var dir = new Vector2(Random01(ref rngState) * 2 - 1, Random01(ref rngState) *2 -1).Normalized;
var ray = new CollisionRay(entWorldPos, dir, (int) CollisionGroup.Impassable);
var rayResult = _physMan.IntersectRay(SelfEntity.Transform.MapID, ray, MaxWalkDistance, SelfEntity);
var rayResults = _physMan.IntersectRay(SelfEntity.Transform.MapID, ray, MaxWalkDistance, SelfEntity).ToList();
if (rayResult.DidHitObject && rayResult.Distance > 1) // hit an impassable object
if (rayResults.Count == 1)
{
// set the new position back from the wall a bit
_walkTargetPos = entWorldPos + dir * (rayResult.Distance - 0.5f);
WalkingPositiveEdge();
return;
var rayResult = rayResults[0];
if (rayResult.Distance > 1) // hit an impassable object
{
// set the new position back from the wall a bit
_walkTargetPos = entWorldPos + dir * (rayResult.Distance - 0.5f);
WalkingPositiveEdge();
return;
}
}
if (!rayResult.DidHitObject) // hit nothing (path clear)
else // hit nothing (path clear)
{
_walkTargetPos = dir * MaxWalkDistance;
WalkingPositiveEdge();

View File

@@ -35,6 +35,7 @@ namespace Content.Server
"IconSmooth",
"SubFloorHide",
"LowWall",
"ReinforcedWall",
"Window",
"CharacterInfo",
"InteractionOutline",

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Utility;
using Content.Shared.Access;
using Content.Shared.GameObjects.Components.Access;
using Robust.Server.GameObjects.Components.Container;

View File

@@ -4,8 +4,10 @@ using Content.Shared.GameObjects.Components.Interactable;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components
{

View File

@@ -14,6 +14,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Content.Server.Utility;
using Robust.Shared.Map;
namespace Content.Server.GameObjects.Components.Cargo

View File

@@ -2,6 +2,7 @@
using Content.Server.GameObjects.Components.Metabolism;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Utility;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Chemistry;
using Robust.Shared.GameObjects;
@@ -110,6 +111,8 @@ namespace Content.Server.GameObjects.Components.Chemistry
/// <param name="eventArgs"></param>
void IAfterAttack.AfterAttack(AfterAttackEventArgs eventArgs)
{
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
//Make sure we have the attacking entity
if (eventArgs.Attacked == null || !_internalContents.Injector)
{

View File

@@ -4,12 +4,15 @@ using System.Text;
using Content.Server.GameObjects.Components.Nutrition;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Utility;
using Content.Shared.Chemistry;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Chemistry
{
@@ -25,6 +28,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
#pragma warning disable 649
[Dependency] private readonly IServerNotifyManager _notifyManager;
[Dependency] private readonly ILocalizationManager _localizationManager;
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
#pragma warning restore 649
public override string Name => "Pourable";

View File

@@ -5,6 +5,7 @@ using Content.Server.GameObjects.EntitySystems;
using Content.Server.GameObjects.Components.Power;
using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Utility;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Chemistry;
using Robust.Server.GameObjects.Components.Container;

View File

@@ -216,21 +216,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
[Verb]
private sealed class FillTargetVerb : Verb<SolutionComponent>
{
protected override string GetText(IEntity user, SolutionComponent component)
{
if(!user.TryGetComponent<HandsComponent>(out var hands))
return "<I SHOULD BE INVISIBLE>";
if(hands.GetActiveHand == null)
return "<I SHOULD BE INVISIBLE>";
var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? "<Item>";
var myName = component.Owner.Prototype?.Name ?? "<Item>";
return $"Transfer liquid from [{heldEntityName}] to [{myName}].";
}
protected override VerbVisibility GetVisibility(IEntity user, SolutionComponent component)
protected override void GetData(IEntity user, SolutionComponent component, VerbData data)
{
if (user.TryGetComponent<HandsComponent>(out var hands))
{
@@ -238,13 +224,20 @@ namespace Content.Server.GameObjects.Components.Chemistry
{
if (hands.GetActiveHand.Owner.TryGetComponent<SolutionComponent>(out var solution))
{
if ((solution.Capabilities & SolutionCaps.PourOut) != 0 && (component.Capabilities & SolutionCaps.PourIn) != 0)
return VerbVisibility.Visible;
if ((solution.Capabilities & SolutionCaps.PourOut) != 0 &&
(component.Capabilities & SolutionCaps.PourIn) != 0)
{
var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? "<Item>";
var myName = component.Owner.Prototype?.Name ?? "<Item>";
data.Text= $"Transfer liquid from [{heldEntityName}] to [{myName}].";
return;
}
}
}
}
return VerbVisibility.Invisible;
data.Visibility = VerbVisibility.Invisible;
}
protected override void Activate(IEntity user, SolutionComponent component)
@@ -269,7 +262,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
var transferSolution = handSolutionComp.SplitSolution(transferQuantity);
component.TryAddSolution(transferSolution);
}
}
@@ -304,21 +296,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
[Verb]
private sealed class EmptyTargetVerb : Verb<SolutionComponent>
{
protected override string GetText(IEntity user, SolutionComponent component)
{
if (!user.TryGetComponent<HandsComponent>(out var hands))
return "<I SHOULD BE INVISIBLE>";
if (hands.GetActiveHand == null)
return "<I SHOULD BE INVISIBLE>";
var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? "<Item>";
var myName = component.Owner.Prototype?.Name ?? "<Item>";
return $"Transfer liquid from [{myName}] to [{heldEntityName}].";
}
protected override VerbVisibility GetVisibility(IEntity user, SolutionComponent component)
protected override void GetData(IEntity user, SolutionComponent component, VerbData data)
{
if (user.TryGetComponent<HandsComponent>(out var hands))
{
@@ -326,13 +304,20 @@ namespace Content.Server.GameObjects.Components.Chemistry
{
if (hands.GetActiveHand.Owner.TryGetComponent<SolutionComponent>(out var solution))
{
if ((solution.Capabilities & SolutionCaps.PourIn) != 0 && (component.Capabilities & SolutionCaps.PourOut) != 0)
return VerbVisibility.Visible;
if ((solution.Capabilities & SolutionCaps.PourIn) != 0 &&
(component.Capabilities & SolutionCaps.PourOut) != 0)
{
var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? "<Item>";
var myName = component.Owner.Prototype?.Name ?? "<Item>";
data.Text = $"Transfer liquid from [{myName}] to [{heldEntityName}].";
return;
}
}
}
}
return VerbVisibility.Invisible;
data.Visibility = VerbVisibility.Invisible;
}
protected override void Activate(IEntity user, SolutionComponent component)

View File

@@ -1,6 +1,7 @@
using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces.GameTicking;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Command;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.Interfaces.GameObjects;

View File

@@ -4,6 +4,7 @@ using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Utility;
using Content.Shared.Construction;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Interactable;
@@ -11,6 +12,7 @@ using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Random;
@@ -19,6 +21,8 @@ using Robust.Shared.Localization;
using Robust.Shared.ViewVariables;
using static Content.Shared.Construction.ConstructionStepMaterial;
using static Content.Shared.Construction.ConstructionStepTool;
using Robust.Shared.Utility;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Construction
{
@@ -47,18 +51,16 @@ namespace Content.Server.GameObjects.Components.Construction
Sprite = Owner.GetComponent<SpriteComponent>();
Transform = Owner.GetComponent<ITransformComponent>();
var systemman = IoCManager.Resolve<IEntitySystemManager>();
}
public bool AttackBy(AttackByEventArgs eventArgs)
{
var playerEntity = eventArgs.User;
var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>();
if (!interactionSystem.InRangeUnobstructed(playerEntity.Transform.MapPosition, Owner.Transform.WorldPosition, ignoredEnt: Owner, insideBlockerValid: Prototype.CanBuildInImpassable))
// default interaction check for AttackBy allows inside blockers, so we will check if its blocked if
// we're not allowed to build on impassable stuff
if (Prototype.CanBuildInImpassable == false)
{
_notifyManager.PopupMessage(Owner.Transform.GridPosition, playerEntity,
_localizationManager.GetString("You can't reach there!"));
return false;
if (!InteractionChecks.InRangeUnobstructed(eventArgs, false))
return false;
}
var stage = Prototype.Stages[Stage];
@@ -125,7 +127,7 @@ namespace Content.Server.GameObjects.Components.Construction
{
return false;
}
var sound = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
var sound = EntitySystem.Get<AudioSystem>();
switch (step)
{

View File

@@ -1,12 +1,14 @@
using System;
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.Construction;
using Content.Shared.GameObjects.Components.Construction;
using Content.Shared.Interfaces;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network;
@@ -16,6 +18,7 @@ using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Players;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Construction
{
@@ -47,16 +50,13 @@ namespace Content.Server.GameObjects.Components.Construction
{
var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName);
var transform = Owner.Transform;
var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>();
if (!interactionSystem.InRangeUnobstructed(loc.ToMap(_mapManager), Owner.Transform.WorldPosition, ignoredEnt: Owner, insideBlockerValid: prototype.CanBuildInImpassable))
if (!InteractionChecks.InRangeUnobstructed(Owner, loc.ToMapPos(_mapManager),
ignoredEnt: Owner, insideBlockerValid: prototype.CanBuildInImpassable))
{
_notifyManager.PopupMessage(transform.GridPosition, Owner,
_localizationManager.GetString("You can't reach there!"));
return;
}
if (prototype.Stages.Count < 2)
{
throw new InvalidOperationException($"Prototype '{prototypeName}' does not have enough stages.");

View File

@@ -5,6 +5,7 @@ using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.Components.VendingMachines;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Doors;
using Content.Shared.GameObjects.Components.Interactable;
using Robust.Server.GameObjects;

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Access;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Doors;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
@@ -17,7 +19,7 @@ namespace Content.Server.GameObjects
{
[RegisterComponent]
[ComponentReference(typeof(IActivate))]
public class ServerDoorComponent : Component, IActivate
public class ServerDoorComponent : Component, IActivate, ICollideBehavior
{
public override string Name => "Door";
@@ -85,26 +87,16 @@ namespace Content.Server.GameObjects
ActivateImpl(eventArgs);
}
public override void HandleMessage(ComponentMessage message, IComponent component)
void ICollideBehavior.CollideWith(IEntity entity)
{
base.HandleMessage(message, component);
switch (message)
if (State != DoorState.Closed)
{
case BumpedEntMsg msg:
if (State != DoorState.Closed)
{
return;
}
// Only open when bumped by mobs.
if (!msg.Entity.HasComponent(typeof(SpeciesComponent)))
{
return;
}
TryOpen(msg.Entity);
break;
return;
}
if (entity.HasComponent(typeof(SpeciesComponent)))
{
TryOpen(entity);
}
}
@@ -155,7 +147,7 @@ namespace Content.Server.GameObjects
Timer.Spawn(OpenTimeOne, async () =>
{
collidableComponent.IsHardCollidable = false;
collidableComponent.CanCollide = false;
await Timer.Delay(OpenTimeTwo, _cancellationTokenSource.Token);
@@ -191,14 +183,14 @@ namespace Content.Server.GameObjects
public bool Close()
{
if (collidableComponent.TryCollision(Vector2.Zero))
if (collidableComponent.IsColliding(Vector2.Zero))
{
// Do nothing, somebody's in the door.
return false;
}
State = DoorState.Closing;
collidableComponent.IsHardCollidable = true;
collidableComponent.CanCollide = true;
OpenTimeCounter = 0;
SetAppearance(DoorVisualState.Closing);

View File

@@ -2,6 +2,7 @@ using System;
using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.Chemistry;
using Content.Shared.Interfaces;
using Robust.Shared.GameObjects;

View File

@@ -19,19 +19,18 @@ namespace Content.Server.GameObjects.Components.Fluids
[Verb]
private sealed class FillTargetVerb : Verb<CanSpillComponent>
{
protected override string GetText(IEntity user, CanSpillComponent component)
protected override void GetData(IEntity user, CanSpillComponent component, VerbData data)
{
return "Spill liquid";
}
protected override VerbVisibility GetVisibility(IEntity user, CanSpillComponent component)
{
if (component.Owner.TryGetComponent(out SolutionComponent solutionComponent))
if (!component.Owner.TryGetComponent(out SolutionComponent solutionComponent))
{
return solutionComponent.CurrentVolume > ReagentUnit.Zero ? VerbVisibility.Visible : VerbVisibility.Disabled;
data.Visibility = VerbVisibility.Invisible;
return;
}
return VerbVisibility.Invisible;
data.Text = "Spill liquid";
data.Visibility = solutionComponent.CurrentVolume > ReagentUnit.Zero
? VerbVisibility.Visible
: VerbVisibility.Disabled;
}
protected override void Activate(IEntity user, CanSpillComponent component)

View File

@@ -2,6 +2,7 @@ using System;
using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.Chemistry;
using Content.Shared.Interfaces;
using Robust.Shared.GameObjects;
@@ -59,6 +60,8 @@ namespace Content.Server.GameObjects.Components.Fluids
void IAfterAttack.AfterAttack(AfterAttackEventArgs eventArgs)
{
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
Solution solution;
if (eventArgs.Attacked == null)
{

View File

@@ -9,6 +9,7 @@ using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
@@ -17,6 +18,7 @@ using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using Timer = Robust.Shared.Timers.Timer;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Fluids
{
@@ -34,7 +36,7 @@ namespace Content.Server.GameObjects.Components.Fluids
// Small puddles will evaporate after a set delay
// TODO: 'leaves fluidtracks', probably in a separate component for stuff like gibb chunks?;
// TODO: Add stuff like slipping -> probably in a separate component (for stuff like bananas) and using BumpEntMsg
// TODO: Add stuff like slipping -> probably in a separate component (for stuff like bananas)
// based on behaviour (e.g. someone being punched vs slashed with a sword would have different blood sprite)
// to check for low volumes for evaporation or whatever
@@ -137,8 +139,7 @@ namespace Content.Server.GameObjects.Components.Fluids
return true;
}
var entitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
entitySystemManager.GetEntitySystem<AudioSystem>().Play(_spillSound);
EntitySystem.Get<AudioSystem>().Play(_spillSound);
return true;
}

View File

@@ -3,6 +3,7 @@ using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Gravity;
using Content.Shared.GameObjects.Components.Interactable;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
@@ -12,10 +13,12 @@ using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Gravity
{
@@ -114,10 +117,8 @@ namespace Content.Server.GameObjects.Components.Gravity
breakable.broken = false;
_intact = true;
var entitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
var notifyManager = IoCManager.Resolve<IServerNotifyManager>();
entitySystemManager.GetEntitySystem<AudioSystem>().Play("/Audio/items/welder2.ogg", Owner);
notifyManager.PopupMessage(Owner, eventArgs.User, Loc.GetString("You repair the gravity generator with the welder"));
return true;

View File

@@ -1,5 +1,6 @@
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
@@ -24,6 +25,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
void IAfterAttack.AfterAttack(AfterAttackEventArgs eventArgs)
{
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
if (eventArgs.Attacked == null)
{
return;

View File

@@ -1,15 +1,26 @@
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Mobs;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Instruments;
using NFluidsynth;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Server.Player;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using Logger = Robust.Shared.Log.Logger;
using MidiEvent = Robust.Shared.Audio.Midi.MidiEvent;
namespace Content.Server.GameObjects.Components.Instruments
{
@@ -18,12 +29,35 @@ namespace Content.Server.GameObjects.Components.Instruments
public class InstrumentComponent : SharedInstrumentComponent,
IDropped, IHandSelected, IHandDeselected, IActivate, IUse, IThrown
{
[Dependency] private IServerNotifyManager _notifyManager;
// These 2 values are quite high for now, and this could be easily abused. Change this if people are abusing it.
public const int MaxMidiEventsPerSecond = 20;
public const int MaxMidiEventsPerBatch = 50;
public const int MaxMidiBatchDropped = 20;
/// <summary>
/// The client channel currently playing the instrument, or null if there's none.
/// </summary>
private ICommonSession _instrumentPlayer;
[ViewVariables]
private IPlayerSession _instrumentPlayer;
private bool _handheld;
[ViewVariables]
private bool _playing = false;
[ViewVariables]
private float _timer = 0f;
[ViewVariables]
private int _batchesDropped = 0;
[ViewVariables]
private uint _lastSequencerTick = 0;
[ViewVariables]
private int _midiEventCount = 0;
[ViewVariables]
private BoundUserInterface _userInterface;
@@ -33,6 +67,43 @@ namespace Content.Server.GameObjects.Components.Instruments
[ViewVariables]
public bool Handheld => _handheld;
/// <summary>
/// Whether the instrument is currently playing or not.
/// </summary>
[ViewVariables]
public bool Playing
{
get => _playing;
set
{
_playing = value;
Dirty();
}
}
public IPlayerSession InstrumentPlayer
{
get => _instrumentPlayer;
private set
{
Playing = false;
if(_instrumentPlayer != null)
_instrumentPlayer.PlayerStatusChanged -= OnPlayerStatusChanged;
_instrumentPlayer = value;
if(value != null)
_instrumentPlayer.PlayerStatusChanged += OnPlayerStatusChanged;
}
}
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (e.NewStatus == SessionStatus.Disconnected)
InstrumentPlayer = null;
}
public override void Initialize()
{
base.Initialize();
@@ -46,31 +117,52 @@ namespace Content.Server.GameObjects.Components.Instruments
serializer.DataField(ref _handheld, "handheld", false);
}
public override ComponentState GetComponentState()
{
return new InstrumentState(Playing, _lastSequencerTick);
}
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null)
{
base.HandleNetworkMessage(message, channel, session);
// If the client that sent the message isn't the client playing this instrument, we ignore it.
if (session != _instrumentPlayer) return;
switch (message)
{
case InstrumentMidiEventMessage midiEventMsg:
SendNetworkMessage(midiEventMsg);
if (!Playing || session != _instrumentPlayer)
return;
if (++_midiEventCount <= MaxMidiEventsPerSecond &&
midiEventMsg.MidiEvent.Length < MaxMidiEventsPerBatch)
SendNetworkMessage(midiEventMsg);
else
_batchesDropped++; // Batch dropped!
_lastSequencerTick = midiEventMsg.MidiEvent[^1].Timestamp;
break;
case InstrumentStartMidiMessage startMidi:
Playing = true;
break;
case InstrumentStopMidiMessage stopMidi:
Playing = false;
_lastSequencerTick = 0;
break;
}
}
public void Dropped(DroppedEventArgs eventArgs)
{
Playing = false;
SendNetworkMessage(new InstrumentStopMidiMessage());
_instrumentPlayer = null;
InstrumentPlayer = null;
_userInterface.CloseAll();
}
public void Thrown(ThrownEventArgs eventArgs)
{
Playing = false;
SendNetworkMessage(new InstrumentStopMidiMessage());
_instrumentPlayer = null;
InstrumentPlayer = null;
_userInterface.CloseAll();
}
@@ -80,11 +172,12 @@ namespace Content.Server.GameObjects.Components.Instruments
if (session == null) return;
_instrumentPlayer = session;
InstrumentPlayer = session;
}
public void HandDeselected(HandDeselectedEventArgs eventArgs)
{
Playing = false;
SendNetworkMessage(new InstrumentStopMidiMessage());
_userInterface.CloseAll();
}
@@ -94,10 +187,10 @@ namespace Content.Server.GameObjects.Components.Instruments
if (Handheld || !eventArgs.User.TryGetComponent(out IActorComponent actor))
return;
if (_instrumentPlayer != null)
if (InstrumentPlayer != null)
return;
_instrumentPlayer = actor.playerSession;
InstrumentPlayer = actor.playerSession;
OpenUserInterface(actor.playerSession);
}
@@ -106,17 +199,18 @@ namespace Content.Server.GameObjects.Components.Instruments
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
return false;
if(_instrumentPlayer == actor.playerSession)
if(InstrumentPlayer == actor.playerSession)
OpenUserInterface(actor.playerSession);
return false;
}
private void UserInterfaceOnClosed(IPlayerSession player)
{
if (!Handheld && player == _instrumentPlayer)
if (!Handheld && player == InstrumentPlayer)
{
_instrumentPlayer = null;
InstrumentPlayer = null;
SendNetworkMessage(new InstrumentStopMidiMessage());
Playing = false;
}
}
@@ -124,5 +218,35 @@ namespace Content.Server.GameObjects.Components.Instruments
{
_userInterface.Open(session);
}
public override void Update(float delta)
{
base.Update(delta);
if (_instrumentPlayer != null && !ActionBlockerSystem.CanInteract(_instrumentPlayer.AttachedEntity))
InstrumentPlayer = null;
if (_batchesDropped > MaxMidiBatchDropped && InstrumentPlayer != null)
{
_batchesDropped = 0;
var mob = InstrumentPlayer.AttachedEntity;
_userInterface.CloseAll();
if (mob.TryGetComponent(out StunnableComponent stun))
stun.Stun(1);
else
StandingStateHelper.DropAllItemsInHands(mob);
InstrumentPlayer = null;
_notifyManager.PopupMessage(Owner, mob, "Your fingers cramp up from playing!");
}
_timer += delta;
if (_timer < 1) return;
_timer = 0f;
_midiEventCount = 0;
}
}
}

View File

@@ -2,6 +2,7 @@
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components;
using Content.Shared.Interfaces;
@@ -250,14 +251,17 @@ namespace Content.Server.GameObjects.Components.Interactable
[Verb]
public sealed class EjectCellVerb : Verb<HandheldLightComponent>
{
protected override string GetText(IEntity user, HandheldLightComponent component)
protected override void GetData(IEntity user, HandheldLightComponent component, VerbData data)
{
return component.Cell == null ? "Eject cell (cell missing)" : "Eject cell";
}
protected override VerbVisibility GetVisibility(IEntity user, HandheldLightComponent component)
{
return component.Cell == null ? VerbVisibility.Disabled : VerbVisibility.Visible;
if (component.Cell == null)
{
data.Text = "Eject cell (cell missing)";
data.Visibility = VerbVisibility.Disabled;
}
else
{
data.Text = "Eject cell";
}
}
protected override void Activate(IEntity user, HandheldLightComponent component)

View File

@@ -1,5 +1,6 @@
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;

View File

@@ -1,5 +1,6 @@
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.Maps;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
@@ -40,18 +41,11 @@ namespace Content.Server.GameObjects.Components.Items
}
public void AfterAttack(AfterAttackEventArgs eventArgs)
{
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
var attacked = eventArgs.Attacked;
var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GridID);
var tile = mapGrid.GetTileRef(eventArgs.ClickLocation);
var coordinates = mapGrid.GridTileToLocal(tile.GridIndices);
float distance = coordinates.Distance(_mapManager, Owner.Transform.GridPosition);
if (distance > InteractionSystem.InteractionRange)
{
return;
}
var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId];
if (tileDef.IsSubFloor && attacked == null && Stack.Use(1))
{
@@ -63,7 +57,7 @@ namespace Content.Server.GameObjects.Components.Items
}
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Linq;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Storage;
using Robust.Server.GameObjects;
@@ -190,7 +191,7 @@ namespace Content.Server.GameObjects.Components
{
if (Owner.TryGetComponent<ICollidableComponent>(out var collidableComponent))
{
collidableComponent.CollisionEnabled = IsCollidableWhenOpen || !Open;
collidableComponent.CanCollide = IsCollidableWhenOpen || !Open;
}
if (Owner.TryGetComponent<PlaceableSurfaceComponent>(out var placeableSurfaceComponent))
@@ -250,7 +251,7 @@ namespace Content.Server.GameObjects.Components
entity.Transform.WorldPosition = worldPos;
if (entityCollidableComponent != null)
{
entityCollidableComponent.CollisionEnabled = false;
entityCollidableComponent.CanCollide = false;
}
return true;
}
@@ -265,7 +266,7 @@ namespace Content.Server.GameObjects.Components
{
if (contained.TryGetComponent<ICollidableComponent>(out var entityCollidableComponent))
{
entityCollidableComponent.CollisionEnabled = true;
entityCollidableComponent.CanCollide = true;
}
}
}
@@ -328,16 +329,9 @@ namespace Content.Server.GameObjects.Components
[Verb]
private sealed class LockToggleVerb : Verb<EntityStorageComponent>
{
/// <inheritdoc />
protected override string GetText(IEntity user, EntityStorageComponent component)
protected override void GetData(IEntity user, EntityStorageComponent component, VerbData data)
{
return component._locked ? "Unlock" : "Lock";
}
/// <inheritdoc />
protected override VerbVisibility GetVisibility(IEntity user, EntityStorageComponent component)
{
return VerbVisibility.Visible;
data.Text = component._locked ? "Unlock" : "Lock";
}
/// <inheritdoc />
@@ -350,16 +344,15 @@ namespace Content.Server.GameObjects.Components
[Verb]
private sealed class OpenToggleVerb : Verb<EntityStorageComponent>
{
/// <inheritdoc />
protected override string GetText(IEntity user, EntityStorageComponent component)
protected override void GetData(IEntity user, EntityStorageComponent component, VerbData data)
{
return component.Open ? "Close" : "Open";
}
if (component.NoDoor)
{
data.Visibility = VerbVisibility.Invisible;
return;
}
/// <inheritdoc />
protected override VerbVisibility GetVisibility(IEntity user, EntityStorageComponent component)
{
return component.NoDoor ? VerbVisibility.Invisible : VerbVisibility.Visible;
data.Text = component.Open ? "Close" : "Open";
}
/// <inheritdoc />

View File

@@ -0,0 +1,65 @@
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Random;
namespace Content.Server.GameObjects.Components.Items.Storage.Fill
{
[RegisterComponent]
internal sealed class EmergencyClosetFillComponent : Component, IMapInit
{
public override string Name => "EmergencyClosetFill";
void IMapInit.MapInit()
{
var storage = Owner.GetComponent<IStorageComponent>();
var random = IoCManager.Resolve<IRobustRandom>();
void Spawn(string prototype)
{
storage.Insert(Owner.EntityManager.SpawnEntity(prototype, Owner.Transform.GridPosition));
}
if (random.Prob(40))
{
Spawn("ToolboxEmergencyFilled");
}
var pick = random.Next(0, 100);
if (pick < 40) // 40%
{
// TODO: uncomment when we actually have these items.
// Spawn("TankOxygenSmallFilled");
// Spawn("TankOxygenSmallFilled");
Spawn("BreathMaskClothing");
Spawn("BreathMaskClothing");
}
else if (pick < 65) // 25%
{
// Spawn("TankOxygenSmallFilled");
// Spawn("MedkitOxygenFilled");
Spawn("BreathMaskClothing");
}
else if (pick < 85) // 20%
{
// Spawn("TankOxygenFilled");
Spawn("BreathMaskClothing");
}
else if (pick < 95) // 10%
{
// Spawn("TankOxygenSmallFilled");
Spawn("BreathMaskClothing");
}
else if (pick < 99) // 4%
{
// nothing, doot
}
else // 1%
{
// teehee
Owner.Delete();
}
}
}
}

View File

@@ -12,10 +12,6 @@ namespace Content.Server.GameObjects.Components.Items.Storage.Fill
{
public override string Name => "ToolLockerFill";
#pragma warning disable 649
[Dependency] private readonly IEntityManager _entityManager;
#pragma warning restore 649
void IMapInit.MapInit()
{
var storage = Owner.GetComponent<IStorageComponent>();
@@ -23,7 +19,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage.Fill
void Spawn(string prototype)
{
storage.Insert(_entityManager.SpawnEntity(prototype, Owner.Transform.GridPosition));
storage.Insert(Owner.EntityManager.SpawnEntity(prototype, Owner.Transform.GridPosition));
}
if (random.Prob(0.4f))

View File

@@ -4,6 +4,7 @@ using Content.Server.GameObjects.Components.Destructible;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Throw;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items;
using Content.Shared.Physics;
@@ -12,6 +13,7 @@ using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Map;
@@ -23,6 +25,7 @@ using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects
{
@@ -35,7 +38,6 @@ namespace Content.Server.GameObjects
#pragma warning disable 649
[Dependency] private readonly IRobustRandom _robustRandom;
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
[Dependency] private readonly IMapManager _mapManager;
#pragma warning restore 649
@@ -97,8 +99,7 @@ namespace Content.Server.GameObjects
var userPos = user.Transform.MapPosition;
var itemPos = Owner.Transform.WorldPosition;
return _entitySystemManager.GetEntitySystem<InteractionSystem>()
.InRangeUnobstructed(userPos, itemPos, ignoredEnt: Owner, insideBlockerValid:true);
return InteractionChecks.InRangeUnobstructed(user, itemPos, ignoredEnt: Owner, insideBlockerValid:true);
}
public bool AttackHand(AttackHandEventArgs eventArgs)
@@ -113,23 +114,15 @@ namespace Content.Server.GameObjects
[Verb]
public sealed class PickUpVerb : Verb<ItemComponent>
{
protected override string GetText(IEntity user, ItemComponent component)
{
if (user.TryGetComponent(out HandsComponent hands) && hands.IsHolding(component.Owner))
{
return "Pick Up (Already Holding)";
}
return "Pick Up";
}
protected override VerbVisibility GetVisibility(IEntity user, ItemComponent component)
protected override void GetData(IEntity user, ItemComponent component, VerbData data)
{
if (ContainerHelpers.IsInContainer(component.Owner) || !component.CanPickup(user))
{
return VerbVisibility.Invisible;
data.Visibility = VerbVisibility.Invisible;
return;
}
return VerbVisibility.Visible;
data.Text = "Pick Up";
}
protected override void Activate(IEntity user, ItemComponent component)

View File

@@ -5,6 +5,7 @@ using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Storage;
using Content.Shared.Interfaces;
using Robust.Server.GameObjects;
@@ -149,6 +150,8 @@ namespace Content.Server.GameObjects
return false;
}
return PlayerInsertEntity(eventArgs.User);
}

View File

@@ -21,6 +21,7 @@ using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.Localization;
using Content.Server.Interfaces;
using Content.Server.Utility;
using Robust.Shared.Audio;
namespace Content.Server.GameObjects.Components.Kitchen
@@ -177,6 +178,7 @@ namespace Content.Server.GameObjects.Components.Kitchen
{
return;
}
UpdateUserInterface();
_userInterface.Open(actor.playerSession);
@@ -224,6 +226,8 @@ namespace Content.Server.GameObjects.Components.Kitchen
if (!itemEntity.TryGetComponent(typeof(FoodComponent), out var food))
{
_notifyManager.PopupMessage(Owner, eventArgs.User, "That won't work!");
return false;
}

View File

@@ -1,5 +1,6 @@
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components;
using Content.Shared.Interfaces;
using Content.Shared.Preferences.Appearance;

View File

@@ -4,6 +4,7 @@ using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Medical;
using Content.Server.GameObjects.Components.Power;
using Content.Server.Utility;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.Components.UserInterface;
@@ -120,22 +121,20 @@ namespace Content.Server.GameObjects.Components.Medical
{
return;
}
if (!Powered)
return;
_userInterface.Open(actor.playerSession);
}
[Verb]
public sealed class EnterVerb : Verb<MedicalScannerComponent>
{
protected override string GetText(IEntity user, MedicalScannerComponent component)
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
{
return "Enter";
}
protected override VerbVisibility GetVisibility(IEntity user, MedicalScannerComponent component)
{
return component.IsOccupied ? VerbVisibility.Invisible : VerbVisibility.Visible;
data.Text = "Enter";
data.Visibility = component.IsOccupied ? VerbVisibility.Invisible : VerbVisibility.Visible;
}
protected override void Activate(IEntity user, MedicalScannerComponent component)
@@ -147,14 +146,10 @@ namespace Content.Server.GameObjects.Components.Medical
[Verb]
public sealed class EjectVerb : Verb<MedicalScannerComponent>
{
protected override string GetText(IEntity user, MedicalScannerComponent component)
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
{
return "Eject";
}
protected override VerbVisibility GetVisibility(IEntity user, MedicalScannerComponent component)
{
return component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible;
data.Text = "Eject";
data.Visibility = component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible;
}
protected override void Activate(IEntity user, MedicalScannerComponent component)

View File

@@ -1,6 +1,7 @@
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.Components.Weapon.Melee;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;

View File

@@ -195,7 +195,7 @@ namespace Content.Server.GameObjects
if (entity.TryGetComponent(out CollidableComponent collidable))
{
collidable.CollisionEnabled = false;
collidable.CanCollide = false;
}
}
@@ -205,7 +205,7 @@ namespace Content.Server.GameObjects
if (entity.TryGetComponent(out CollidableComponent collidable))
{
collidable.CollisionEnabled = true;
collidable.CanCollide = true;
}
}

View File

@@ -6,11 +6,13 @@ using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.Timers;
using Robust.Shared.ViewVariables;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Movement
{
@@ -63,7 +65,7 @@ namespace Content.Server.GameObjects.Components.Movement
base.OnAdd();
if (Owner.TryGetComponent<CollidableComponent>(out var collide))
{
collide.IsHardCollidable = false;
//collide.IsHardCollidable = false;
}
_state = PortalState.Pending;
@@ -195,7 +197,7 @@ namespace Content.Server.GameObjects.Components.Movement
if (!immuneEntities.Contains(entity))
{
var position = _connectingTeleporter.Transform.GridPosition;
var soundPlayer = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
var soundPlayer = EntitySystem.Get<AudioSystem>();
// Departure
// Do we need to rate-limit sounds to stop ear BLAST?

View File

@@ -7,6 +7,7 @@ using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Random;
@@ -16,6 +17,7 @@ using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.Timers;
using Robust.Shared.ViewVariables;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Movement
{
@@ -141,7 +143,7 @@ namespace Content.Server.GameObjects.Components.Movement
Timer.Spawn(TimeSpan.FromSeconds(_chargeTime + _cooldown), () => SetState(ItemTeleporterState.Off));
if (_cooldownSound != null)
{
var soundPlayer = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
var soundPlayer = EntitySystem.Get<AudioSystem>();
soundPlayer.Play(_cooldownSound, Owner);
}
}
@@ -227,7 +229,7 @@ namespace Content.Server.GameObjects.Components.Movement
{
// Messy maybe?
GridCoordinates targetGrid = new GridCoordinates(vector, user.Transform.GridID);
var soundPlayer = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
var soundPlayer = EntitySystem.Get<AudioSystem>();
// If portals use those, otherwise just move em over
if (_portalAliveTime > 0.0f)

View File

@@ -65,8 +65,8 @@ namespace Content.Server.GameObjects.Components.Movement
if (!gridEntity.HasComponent<ICollidableComponent>())
{
var collideComp = gridEntity.AddComponent<CollidableComponent>();
collideComp.CollisionEnabled = true;
collideComp.IsHardCollidable = true;
collideComp.CanCollide = true;
//collideComp.IsHardCollidable = true;
collideComp.PhysicsShapes.Add(new PhysShapeGrid(grid));
}

View File

@@ -3,18 +3,22 @@ using System.Linq;
using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Nutrition;
using Content.Shared.Interfaces;
using Content.Shared.Maths;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Nutrition
{
@@ -98,23 +102,25 @@ namespace Content.Server.GameObjects.Components.Nutrition
void IAfterAttack.AfterAttack(AfterAttackEventArgs eventArgs)
{
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
UseDrink(eventArgs.Attacked);
}
private void UseDrink(IEntity user)
private void UseDrink(IEntity targetEntity)
{
if (user == null)
if (targetEntity == null)
{
return;
}
if (UsesLeft() == 0 && !_despawnOnFinish)
{
user.PopupMessage(user, _localizationManager.GetString("Empty"));
targetEntity.PopupMessage(targetEntity, _localizationManager.GetString("Empty"));
return;
}
if (user.TryGetComponent(out StomachComponent stomachComponent))
if (targetEntity.TryGetComponent(out StomachComponent stomachComponent))
{
_drinking = true;
var transferAmount = ReagentUnit.Min(_transferAmount, _contents.CurrentVolume);
@@ -124,17 +130,16 @@ namespace Content.Server.GameObjects.Components.Nutrition
// When we split Finish gets called which may delete the can so need to use the entity system for sound
if (_useSound != null)
{
var entitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
var audioSystem = entitySystemManager.GetEntitySystem<AudioSystem>();
audioSystem.Play(_useSound);
user.PopupMessage(user, _localizationManager.GetString("Slurp"));
var audioSystem = EntitySystem.Get<AudioSystem>();
audioSystem.Play(_useSound, Owner, AudioParams.Default.WithVolume(-2f));
targetEntity.PopupMessage(targetEntity, _localizationManager.GetString("Slurp"));
}
}
else
{
// Add it back in
_contents.TryAddSolution(split);
user.PopupMessage(user, _localizationManager.GetString("Can't drink"));
targetEntity.PopupMessage(targetEntity, _localizationManager.GetString("Can't drink"));
}
_drinking = false;
}

View File

@@ -2,6 +2,7 @@
using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Nutrition;
using Content.Shared.Interfaces;
@@ -111,6 +112,8 @@ namespace Content.Server.GameObjects.Components.Nutrition
void IAfterAttack.AfterAttack(AfterAttackEventArgs eventArgs)
{
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
UseFood(eventArgs.Attacked);
}

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.UserInterface;

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;

View File

@@ -1,5 +1,6 @@
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Power;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.UserInterface;

View File

@@ -1,5 +1,6 @@
using System;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Power;
using Content.Shared.Interfaces;
@@ -58,28 +59,22 @@ namespace Content.Server.GameObjects.Components.Power.Chargers
[Verb]
private sealed class InsertVerb : Verb<PowerCellChargerComponent>
{
protected override string GetText(IEntity user, PowerCellChargerComponent component)
{
if (!user.TryGetComponent(out HandsComponent handsComponent) || handsComponent.GetActiveHand == null)
{
return "Insert";
}
return $"Insert {handsComponent.GetActiveHand.Owner.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, PowerCellChargerComponent component)
protected override void GetData(IEntity user, PowerCellChargerComponent component, VerbData data)
{
if (!user.TryGetComponent(out HandsComponent handsComponent))
{
return VerbVisibility.Invisible;
data.Visibility = VerbVisibility.Invisible;
return;
}
if (component._container.ContainedEntity != null || handsComponent.GetActiveHand == null)
{
return VerbVisibility.Disabled;
data.Visibility = VerbVisibility.Disabled;
data.Text = "Insert";
return;
}
return VerbVisibility.Visible;
data.Text = $"Insert {handsComponent.GetActiveHand.Owner.Name}";
}
protected override void Activate(IEntity user, PowerCellChargerComponent component)
@@ -102,22 +97,16 @@ namespace Content.Server.GameObjects.Components.Power.Chargers
[Verb]
private sealed class EjectVerb : Verb<PowerCellChargerComponent>
{
protected override string GetText(IEntity user, PowerCellChargerComponent component)
protected override void GetData(IEntity user, PowerCellChargerComponent component, VerbData data)
{
if (component._container.ContainedEntity == null)
{
return "Eject";
data.Text = "Eject";
data.Visibility = VerbVisibility.Disabled;
return;
}
return $"Eject {component._container.ContainedEntity.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, PowerCellChargerComponent component)
{
if (component._container.ContainedEntity == null)
{
return VerbVisibility.Disabled;
}
return VerbVisibility.Visible;
data.Text = $"Eject {component._container.ContainedEntity.Name}";
}
protected override void Activate(IEntity user, PowerCellChargerComponent component)

View File

@@ -1,6 +1,7 @@
using System;
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Power;
using Content.Shared.Interfaces;
@@ -48,28 +49,27 @@ namespace Content.Server.GameObjects.Components.Power.Chargers
[Verb]
private sealed class InsertVerb : Verb<WeaponCapacitorChargerComponent>
{
protected override string GetText(IEntity user, WeaponCapacitorChargerComponent component)
{
if (!user.TryGetComponent(out HandsComponent handsComponent) || handsComponent.GetActiveHand == null)
{
return "Insert";
}
return $"Insert {handsComponent.GetActiveHand.Owner.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, WeaponCapacitorChargerComponent component)
protected override void GetData(IEntity user, WeaponCapacitorChargerComponent component, VerbData data)
{
if (!user.TryGetComponent(out HandsComponent handsComponent))
{
return VerbVisibility.Invisible;
data.Visibility = VerbVisibility.Invisible;
return;
}
if (component._container.ContainedEntity != null || handsComponent.GetActiveHand == null)
if (handsComponent.GetActiveHand == null)
{
return VerbVisibility.Disabled;
data.Visibility = VerbVisibility.Disabled;
data.Text = "Insert";
return;
}
return VerbVisibility.Visible;
if (component._container.ContainedEntity != null)
{
data.Visibility = VerbVisibility.Disabled;
}
data.Text = $"Insert {handsComponent.GetActiveHand.Owner.Name}";
}
protected override void Activate(IEntity user, WeaponCapacitorChargerComponent component)
@@ -92,22 +92,16 @@ namespace Content.Server.GameObjects.Components.Power.Chargers
[Verb]
private sealed class EjectVerb : Verb<WeaponCapacitorChargerComponent>
{
protected override string GetText(IEntity user, WeaponCapacitorChargerComponent component)
protected override void GetData(IEntity user, WeaponCapacitorChargerComponent component, VerbData data)
{
if (component._container.ContainedEntity == null)
{
return "Eject";
data.Visibility = VerbVisibility.Disabled;
data.Text = "Eject";
return;
}
return $"Eject {component._container.ContainedEntity.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, WeaponCapacitorChargerComponent component)
{
if (component._container.ContainedEntity == null)
{
return VerbVisibility.Disabled;
}
return VerbVisibility.Visible;
data.Text = $"Eject {component._container.ContainedEntity.Name}";
}
protected override void Activate(IEntity user, WeaponCapacitorChargerComponent component)

View File

@@ -4,6 +4,7 @@ using Content.Shared.Audio;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
@@ -12,6 +13,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Power
{
@@ -126,7 +128,7 @@ namespace Content.Server.GameObjects.Components.Power
var soundCollection = _prototypeManager.Index<SoundCollectionPrototype>("glassbreak");
var file = _random.Pick(soundCollection.PickFiles);
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>().Play(file, Owner);
EntitySystem.Get<AudioSystem>().Play(file, Owner);
State = LightBulbState.Broken;
}

View File

@@ -3,6 +3,7 @@ using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Components.Interactable;
using Content.Server.Utility;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;

View File

@@ -1,6 +1,7 @@
using System;
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;

View File

@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.ViewVariables;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Power
{
@@ -15,8 +17,7 @@ namespace Content.Server.GameObjects.Components.Power
{
public Powernet()
{
var EntitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
var powerSystem = EntitySystemManager.GetEntitySystem<PowerSystem>();
var powerSystem = EntitySystem.Get<PowerSystem>();
powerSystem.Powernets.Add(this);
Uid = powerSystem.NewUid();
}
@@ -374,8 +375,7 @@ namespace Content.Server.GameObjects.Components.Power
/// </summary>
private void RemoveFromSystem()
{
var EntitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
EntitySystemManager.GetEntitySystem<PowerSystem>().Powernets.Remove(this);
EntitySystem.Get<PowerSystem>().Powernets.Remove(this);
}
#region Registration

View File

@@ -1,5 +1,6 @@
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Robust.Server.GameObjects;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
@@ -25,6 +26,8 @@ namespace Content.Server.GameObjects.Components.Power
/// <inheritdoc />
public void AfterAttack(AfterAttackEventArgs eventArgs)
{
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
if(!_mapManager.TryGetGrid(eventArgs.ClickLocation.GridID, out var grid))
return;

View File

@@ -62,36 +62,32 @@ namespace Content.Server.GameObjects.Components.Projectiles
}
/// <summary>
/// Applys the damage when our projectile collides with its victim
/// Applies the damage when our projectile collides with its victim
/// </summary>
/// <param name="collidedwith"></param>
void ICollideBehavior.CollideWith(List<IEntity> collidedwith)
/// <param name="entity"></param>
void ICollideBehavior.CollideWith(IEntity entity)
{
foreach (var entity in collidedwith)
if (entity.TryGetComponent(out DamageableComponent damage))
{
if (entity.TryGetComponent(out DamageableComponent damage))
Owner.EntityManager.TryGetEntity(Shooter, out var shooter);
foreach (var (damageType, amount) in _damages)
{
Owner.EntityManager.TryGetEntity(Shooter, out var shooter);
foreach (var (damageType, amount) in _damages)
{
damage.TakeDamage(damageType, amount, Owner, shooter);
}
}
if (!entity.Deleted && entity.TryGetComponent(out CameraRecoilComponent recoilComponent)
&& Owner.TryGetComponent(out PhysicsComponent physicsComponent))
{
var direction = physicsComponent.LinearVelocity.Normalized;
recoilComponent.Kick(direction);
damage.TakeDamage(damageType, amount, Owner, shooter);
}
}
if (collidedwith.Count > 0)
if (!entity.Deleted && entity.TryGetComponent(out CameraRecoilComponent recoilComponent)
&& Owner.TryGetComponent(out PhysicsComponent physicsComponent))
{
Owner.Delete();
var direction = physicsComponent.LinearVelocity.Normalized;
recoilComponent.Kick(direction);
}
}
void ICollideBehavior.PostCollide(int collideCount)
{
if (collideCount > 0) Owner.Delete();
}
}
}

View File

@@ -18,6 +18,8 @@ namespace Content.Server.GameObjects.Components
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
#pragma warning restore 649
private bool _shouldCollide = true;
public override string Name => "ThrownItem";
/// <summary>
@@ -25,32 +27,37 @@ namespace Content.Server.GameObjects.Components
/// </summary>
public IEntity User;
void ICollideBehavior.CollideWith(List<IEntity> collidedwith)
void ICollideBehavior.CollideWith(IEntity entity)
{
foreach (var entity in collidedwith)
if (!_shouldCollide) return;
if (entity.TryGetComponent(out DamageableComponent damage))
{
if (entity.TryGetComponent(out DamageableComponent damage))
{
damage.TakeDamage(DamageType.Brute, 10, Owner, User);
}
damage.TakeDamage(DamageType.Brute, 10, Owner, User);
}
// Stop colliding with mobs, this mimics not having enough velocity to do damage
// after impacting the first object.
// For realism this should actually be changed when the velocity of the object is less than a threshold.
// This would allow ricochets off walls, and weird gravity effects from slowing the object.
if (collidedwith.Count > 0 && Owner.TryGetComponent(out CollidableComponent body) && body.PhysicsShapes.Count >= 1)
if (Owner.TryGetComponent(out CollidableComponent body) && body.PhysicsShapes.Count >= 1)
{
_shouldCollide = false;
}
}
public void PostCollide(int collideCount)
{
if (collideCount > 0 && Owner.TryGetComponent(out CollidableComponent body) && body.PhysicsShapes.Count >= 1)
{
body.PhysicsShapes[0].CollisionMask &= (int)~CollisionGroup.MobImpassable;
body.IsScrapingFloor = true;
// KYS, your job is finished. Trigger ILand as well.
var physics = Owner.GetComponent<PhysicsComponent>();
(physics.Controller as ThrowController).StopThrow();
physics.RemoveController();
Owner.RemoveComponent<ThrownItemComponent>();
_entitySystemManager.GetEntitySystem<InteractionSystem>().LandInteraction(User, Owner, Owner.Transform.GridPosition);
}
}
}
}

View File

@@ -0,0 +1,59 @@
using System.Collections.Generic;
using Robust.Server.GameObjects;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components
{
[RegisterComponent]
public class RandomToolColorComponent : Component, IMapInit
{
public override string Name => "RandomToolColor";
private string _selectedColor;
private string _baseState;
private Dictionary<string, Color> _colors;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _selectedColor, "selected", null);
serializer.DataField(ref _baseState, "state", "error");
serializer.DataFieldCached(ref _colors, "colors", new Dictionary<string, Color>());
}
void IMapInit.MapInit()
{
if (_colors == null)
{
return;
}
var random = IoCManager.Resolve<IRobustRandom>();
_selectedColor = random.Pick(_colors.Keys);
UpdateColor();
}
protected override void Startup()
{
base.Startup();
UpdateColor();
}
private void UpdateColor()
{
if (Owner.TryGetComponent(out SpriteComponent spriteComponent) && _colors != null && _selectedColor != null)
{
spriteComponent.LayerSetState(0, _baseState);
spriteComponent.LayerSetColor(0, _colors[_selectedColor]);
}
}
}
}

View File

@@ -8,6 +8,7 @@ using System.Linq;
using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.Components.Stack;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Materials;
using Content.Shared.GameObjects.Components.Power;
using Content.Shared.GameObjects.Components.Research;
@@ -97,7 +98,7 @@ namespace Content.Server.GameObjects.Components.Research
break;
}
}
@@ -150,12 +151,13 @@ namespace Content.Server.GameObjects.Components.Research
{
return;
}
OpenUserInterface(actor.playerSession);
}
bool IAttackBy.AttackBy(AttackByEventArgs eventArgs)
{
if (!Owner.TryGetComponent(out MaterialStorageComponent storage)
|| !eventArgs.AttackWith.TryGetComponent(out MaterialComponent material)) return false;
|| !eventArgs.AttackWith.TryGetComponent(out MaterialComponent material)) return false;
var multiplier = 1;

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Research;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.Interfaces.GameObjects;
@@ -60,7 +61,6 @@ namespace Content.Server.GameObjects.Components.Research
return;
OpenUserInterface(actor.playerSession);
return;
}
public void UpdateUserInterface()

View File

@@ -1,5 +1,6 @@
using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.Audio;
using Content.Shared.GameObjects.Components.Research;
using Content.Shared.Research;
@@ -110,6 +111,7 @@ namespace Content.Server.GameObjects.Components.Research
{
return;
}
OpenUserInterface(actor.playerSession);
PlayKeyboardSound();
return;

View File

@@ -3,10 +3,12 @@ using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Research;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Research
{
@@ -73,7 +75,7 @@ namespace Content.Server.GameObjects.Components.Research
{
base.Initialize();
Id = ServerCount++;
IoCManager.Resolve<IEntitySystemManager>()?.GetEntitySystem<ResearchSystem>()?.RegisterServer(this);
EntitySystem.Get<ResearchSystem>()?.RegisterServer(this);
Database = Owner.GetComponent<TechnologyDatabaseComponent>();
Owner.TryGetComponent(out _powerDevice);
}
@@ -82,7 +84,7 @@ namespace Content.Server.GameObjects.Components.Research
protected override void Shutdown()
{
base.Shutdown();
IoCManager.Resolve<IEntitySystemManager>()?.GetEntitySystem<ResearchSystem>()?.UnregisterServer(this);
EntitySystem.Get<ResearchSystem>()?.UnregisterServer(this);
}
public override void ExposeData(ObjectSerializer serializer)

View File

@@ -35,16 +35,11 @@ namespace Content.Server.GameObjects.Components
[Verb]
public sealed class RotateVerb : Verb<RotatableComponent>
{
protected override string GetText(IEntity user, RotatableComponent component)
protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
{
return "Rotate clockwise";
}
protected override string GetCategory(IEntity user, RotatableComponent component) => "Rotate";
protected override VerbVisibility GetVisibility(IEntity user, RotatableComponent component)
{
return VerbVisibility.Visible;
data.CategoryData = VerbCategories.Rotate;
data.Text = "Rotate clockwise";
data.IconTexture = "/Textures/UserInterface/VerbIcons/rotate_cw.svg.96dpi.png";
}
protected override void Activate(IEntity user, RotatableComponent component)
@@ -56,16 +51,11 @@ namespace Content.Server.GameObjects.Components
[Verb]
public sealed class RotateCounterVerb : Verb<RotatableComponent>
{
protected override string GetText(IEntity user, RotatableComponent component)
protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
{
return "Rotate counter-clockwise";
}
protected override string GetCategory(IEntity user, RotatableComponent component) => "Rotate";
protected override VerbVisibility GetVisibility(IEntity user, RotatableComponent component)
{
return VerbVisibility.Visible;
data.CategoryData = VerbCategories.Rotate;
data.Text = "Rotate counter-clockwise";
data.IconTexture = "/Textures/UserInterface/VerbIcons/rotate_cw.svg.96dpi.png";
}
protected override void Activate(IEntity user, RotatableComponent component)

View File

@@ -1,5 +1,6 @@
using System;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components;
using Content.Shared.Interfaces;
using Robust.Shared.GameObjects;

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.VendingMachines;
using Content.Shared.VendingMachines;
using Robust.Server.GameObjects;

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items;
@@ -140,7 +141,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
for (var i = 0; i < increments; i++)
{
var castAngle = new Angle(baseAngle + increment * i);
var res = _physicsManager.IntersectRay(mapId, new CollisionRay(position, castAngle.ToVec(), 23), _range, ignore, ignoreNonHardCollidables: true);
var res = _physicsManager.IntersectRay(mapId, new CollisionRay(position, castAngle.ToVec(), 23), _range, ignore).First();
if (res.HitEntity != null)
{
resSet.Add(res.HitEntity);

View File

@@ -1,7 +1,9 @@
using System;
using System.Linq;
using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Content.Shared.Interfaces;
using Content.Shared.Physics;
@@ -9,6 +11,7 @@ using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.EntitySystemMessages;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Physics;
using Robust.Shared.Interfaces.Timing;
@@ -17,6 +20,7 @@ using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan
{
@@ -90,10 +94,13 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan
var angle = new Angle(clickLocation.Position - userPosition);
var ray = new CollisionRay(userPosition, angle.ToVec(), (int)(CollisionGroup.Impassable | CollisionGroup.MobImpassable));
var rayCastResults = IoCManager.Resolve<IPhysicsManager>().IntersectRay(user.Transform.MapID, ray, MaxLength, user, ignoreNonHardCollidables: true);
var rayCastResults = IoCManager.Resolve<IPhysicsManager>().IntersectRay(user.Transform.MapID, ray, MaxLength, user).ToList();
Hit(rayCastResults, energyModifier, user);
AfterEffects(user, rayCastResults, angle, energyModifier);
if (rayCastResults.Count == 1)
{
Hit(rayCastResults[0], energyModifier, user);
AfterEffects(user, rayCastResults[0], angle, energyModifier);
}
}
protected virtual void Hit(RayCastResults ray, float damageModifier, IEntity user = null)
@@ -109,7 +116,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan
protected virtual void AfterEffects(IEntity user, RayCastResults ray, Angle angle, float energyModifier)
{
var time = IoCManager.Resolve<IGameTiming>().CurTime;
var dist = ray.DidHitObject ? ray.Distance : MaxLength;
var dist = ray.Distance;
var offset = angle.ToVec() * dist / 2;
var message = new EffectSystemMessage
{
@@ -125,8 +132,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan
Shaded = false
};
var mgr = IoCManager.Resolve<IEntitySystemManager>();
mgr.GetEntitySystem<EffectSystem>().CreateParticle(message);
EntitySystem.Get<EffectSystem>().CreateParticle(message);
Owner.GetComponent<SoundComponent>().Play(_fireSound, AudioParams.Default.WithVolume(-5));
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Content.Shared.Interfaces;
using Robust.Server.GameObjects;

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Content.Shared.Interfaces;
using Robust.Server.GameObjects;

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Sound;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Content.Shared.Interfaces;
@@ -266,14 +267,16 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
[Verb]
public sealed class EjectMagazineVerb : Verb<BallisticMagazineWeaponComponent>
{
protected override string GetText(IEntity user, BallisticMagazineWeaponComponent component)
protected override void GetData(IEntity user, BallisticMagazineWeaponComponent component, VerbData data)
{
return component.Magazine == null ? "Eject magazine (magazine missing)" : "Eject magazine";
}
if (component.Magazine == null)
{
data.Text = "Eject magazine (magazine missing)";
data.Visibility = VerbVisibility.Disabled;
return;
}
protected override VerbVisibility GetVisibility(IEntity user, BallisticMagazineWeaponComponent component)
{
return component.Magazine == null ? VerbVisibility.Disabled : VerbVisibility.Visible;
data.Text = "Eject magazine";
}
protected override void Activate(IEntity user, BallisticMagazineWeaponComponent component)

View File

@@ -42,7 +42,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
{
var angle = GetAngleFromClickLocation(source, coord);
FireAtAngle(source, angle, projectileType, spreadStdDev, projectilesFired, evenSpreadAngle, velocity);
}
}
/// <summary>
/// Fires projectile in the direction of an angle.

View File

@@ -6,6 +6,7 @@ using Content.Server.GameObjects.Components.VendingMachines;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Interactable;
using JetBrains.Annotations;
@@ -14,6 +15,7 @@ using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
@@ -21,6 +23,7 @@ using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components
{
@@ -109,7 +112,7 @@ namespace Content.Server.GameObjects.Components
public override void Initialize()
{
base.Initialize();
_audioSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
_audioSystem = EntitySystem.Get<AudioSystem>();
_appearance = Owner.GetComponent<AppearanceComponent>();
_appearance.SetData(WiresVisuals.MaintenancePanelState, IsPanelOpen);
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
@@ -236,8 +239,7 @@ namespace Content.Server.GameObjects.Components
return;
}
var interactionSystem = IoCManager.Resolve<EntitySystemManager>().GetEntitySystem<InteractionSystem>();
if (!interactionSystem.InRangeUnobstructed(player.Transform.MapPosition, Owner.Transform.WorldPosition, ignoredEnt: Owner))
if (!EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(player.Transform.MapPosition, Owner.Transform.WorldPosition, ignoredEnt: Owner))
{
_notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You can't reach there!"));
return;
@@ -304,8 +306,7 @@ namespace Content.Server.GameObjects.Components
return false;
IsPanelOpen = !IsPanelOpen;
IoCManager.Resolve<IEntitySystemManager>()
.GetEntitySystem<AudioSystem>()
EntitySystem.Get<AudioSystem>()
.Play(IsPanelOpen ? "/Audio/machines/screwdriveropen.ogg" : "/Audio/machines/screwdriverclose.ogg", Owner);
return true;
}

View File

@@ -11,6 +11,7 @@ using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems
{
@@ -101,7 +102,7 @@ namespace Content.Server.GameObjects.EntitySystems
var processorId = args[0];
var entId = new EntityUid(int.Parse(args[1]));
var ent = IoCManager.Resolve<IEntityManager>().GetEntity(entId);
var aiSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AiSystem>();
var aiSystem = EntitySystem.Get<AiSystem>();
if (!aiSystem.ProcessorTypeExists(processorId))
{

View File

@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Timing;
using Content.Server.Interfaces.GameObjects;
using Content.Shared.GameObjects.Components.Interactable;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.Input;
using Content.Shared.Physics;
@@ -21,6 +21,7 @@ using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Physics;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
@@ -30,36 +31,53 @@ namespace Content.Server.GameObjects.EntitySystems
{
/// <summary>
/// This interface gives components behavior when being clicked on or "attacked" by a user with an object in their hand
/// who is in range and has unobstructed reach of the target entity (allows inside blockers).
/// </summary>
public interface IAttackBy
{
/// <summary>
/// Called when using one object on another
/// Called when using one object on another when user is in range of the target entity.
/// </summary>
bool AttackBy(AttackByEventArgs eventArgs);
}
public class AttackByEventArgs : EventArgs
public class AttackByEventArgs : EventArgs, ITargetedAttackEventArgs
{
public IEntity User { get; set; }
public GridCoordinates ClickLocation { get; set; }
public IEntity AttackWith { get; set; }
public IEntity Attacked { get; set; }
}
public interface ITargetedAttackEventArgs
{
/// <summary>
/// Performer of the attack
/// </summary>
IEntity User { get; }
/// <summary>
/// Target of the attack
/// </summary>
IEntity Attacked { get; }
}
/// <summary>
/// This interface gives components behavior when being clicked on or "attacked" by a user with an empty hand
/// who is in range and has unobstructed reach of the target entity (allows inside blockers).
/// </summary>
public interface IAttackHand
{
/// <summary>
/// Called when a player directly interacts with an empty hand
/// Called when a player directly interacts with an empty hand when user is in range of the target entity.
/// </summary>
bool AttackHand(AttackHandEventArgs eventArgs);
}
public class AttackHandEventArgs : EventArgs
public class AttackHandEventArgs : EventArgs, ITargetedAttackEventArgs
{
public IEntity User { get; set; }
public IEntity Attacked { get; set; }
}
/// <summary>
@@ -83,8 +101,8 @@ namespace Content.Server.GameObjects.EntitySystems
}
/// <summary>
/// This interface gives components a behavior when clicking on another object and no interaction occurs
/// Doesn't pass what you clicked on as an argument, but if it becomes necessary we can add it later
/// This interface gives components a behavior when clicking on another object and no interaction occurs,
/// at any range.
/// </summary>
public interface IAfterAttack
{
@@ -119,19 +137,21 @@ namespace Content.Server.GameObjects.EntitySystems
}
/// <summary>
/// This interface gives components behavior when being activated in the world.
/// This interface gives components behavior when being activated in the world when the user
/// is in range and has unobstructed access to the target entity (allows inside blockers).
/// </summary>
public interface IActivate
{
/// <summary>
/// Called when this component is activated by another entity.
/// Called when this component is activated by another entity who is in range.
/// </summary>
void Activate(ActivateEventArgs eventArgs);
}
public class ActivateEventArgs : EventArgs
public class ActivateEventArgs : EventArgs, ITargetedAttackEventArgs
{
public IEntity User { get; set; }
public IEntity Attacked { get; set; }
}
/// <summary>
@@ -295,6 +315,7 @@ namespace Content.Server.GameObjects.EntitySystems
#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly IPhysicsManager _physicsManager;
[Dependency] private readonly ILocalizationManager _localizationManager;
#pragma warning restore 649
public const float InteractionRange = 2;
@@ -362,7 +383,12 @@ namespace Content.Server.GameObjects.EntitySystems
return;
}
activateComp.Activate(new ActivateEventArgs {User = user});
// all activates should only fire when in range / unbostructed
var activateEventArgs = new ActivateEventArgs {User = user, Attacked = used};
if (InteractionChecks.InRangeUnobstructed(activateEventArgs))
{
activateComp.Activate(activateEventArgs);
}
}
private bool HandleWideAttack(ICommonSession session, GridCoordinates coords, EntityUid uid)
@@ -559,14 +585,20 @@ namespace Content.Server.GameObjects.EntitySystems
var attackBys = attacked.GetAllComponents<IAttackBy>().ToList();
var attackByEventArgs = new AttackByEventArgs
{
User = user, ClickLocation = clickLocation, AttackWith = weapon
User = user, ClickLocation = clickLocation, AttackWith = weapon, Attacked = attacked
};
foreach (var attackBy in attackBys)
// all AttackBys should only happen when in range / unobstructed, so no range check is needed
if (InteractionChecks.InRangeUnobstructed(attackByEventArgs))
{
if (attackBy.AttackBy(attackByEventArgs))
// If an AttackBy returns a status completion we finish our attack
return;
foreach (var attackBy in attackBys)
{
if (attackBy.AttackBy(attackByEventArgs))
{
// If an AttackBy returns a status completion we finish our attack
return;
}
}
}
var afterAtkMsg = new AfterAttackMessage(user, weapon, attacked, clickLocation);
@@ -603,14 +635,18 @@ namespace Content.Server.GameObjects.EntitySystems
}
var attackHands = attacked.GetAllComponents<IAttackHand>().ToList();
var attackHandEventArgs = new AttackHandEventArgs {User = user};
var attackHandEventArgs = new AttackHandEventArgs {User = user, Attacked = attacked};
foreach (var attackHand in attackHands)
// all attackHands should only fire when in range / unbostructed
if (InteractionChecks.InRangeUnobstructed(attackHandEventArgs))
{
if (attackHand.AttackHand(attackHandEventArgs))
foreach (var attackHand in attackHands)
{
// If an AttackHand returns a status completion we finish our attack
return;
if (attackHand.AttackHand(attackHandEventArgs))
{
// If an AttackHand returns a status completion we finish our attack
return;
}
}
}

View File

@@ -30,6 +30,7 @@ using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Players;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.EntitySystems
{
@@ -105,7 +106,7 @@ namespace Content.Server.GameObjects.EntitySystems
if (!TryGetAttachedComponent(session as IPlayerSession, out HandsComponent handsComp))
return;
var interactionSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<InteractionSystem>();
var interactionSystem = EntitySystem.Get<InteractionSystem>();
var oldItem = handsComp.GetActiveHand;
@@ -133,9 +134,8 @@ namespace Content.Server.GameObjects.EntitySystems
if (handsComp.GetActiveHand == null)
return false;
var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>();
if(interactionSystem.InRangeUnobstructed(coords.ToMap(_mapManager), ent.Transform.WorldPosition, ignoredEnt: ent))
if(EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(coords.ToMap(_mapManager), ent.Transform.WorldPosition, ignoredEnt: ent))
if (coords.InRange(_mapManager, ent.Transform.GridPosition, InteractionSystem.InteractionRange))
{
handsComp.Drop(handsComp.ActiveIndex, coords);

View File

@@ -0,0 +1,25 @@
using Content.Server.GameObjects.Components.Instruments;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
public class InstrumentSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(InstrumentComponent));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var entity in RelevantEntities)
{
entity.GetComponent<InstrumentComponent>().Update(frameTime);
}
}
}
}

View File

@@ -9,6 +9,7 @@ using Content.Server.Observer;
using Content.Shared.Audio;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.Maps;
using Content.Shared.Physics;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
@@ -148,7 +149,13 @@ namespace Content.Server.GameObjects.EntitySystems
private void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, PhysicsComponent physics, CollidableComponent collider = null)
{
bool weightless = false;
if (physics.Controller == null)
{
// Set up controller
physics.SetController<MoverController>();
}
var weightless = false;
var tile = _mapManager.GetGrid(transform.GridID).GetTileRef(transform.GridPosition).Tile;
@@ -167,27 +174,27 @@ namespace Content.Server.GameObjects.EntitySystems
&& !entity.HasComponent<ItemComponent>(); // This can't be an item
}
}
if (!touching)
{
return;
}
}
if (mover.VelocityDir.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner))
{
if (physics.LinearVelocity != Vector2.Zero)
physics.LinearVelocity = Vector2.Zero;
if (mover.VelocityDir.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner) && !weightless)
{
(physics.Controller as MoverController)?.StopMoving();
}
else
{
if (weightless)
{
physics.LinearVelocity = mover.VelocityDir * mover.CurrentPushSpeed;
(physics.Controller as MoverController)?.Push(mover.VelocityDir, mover.CurrentPushSpeed);
transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle();
return;
}
physics.LinearVelocity = mover.VelocityDir * (mover.Sprinting ? mover.CurrentSprintSpeed : mover.CurrentWalkSpeed);
(physics.Controller as MoverController)?.Move(mover.VelocityDir,
mover.Sprinting ? mover.CurrentSprintSpeed : mover.CurrentWalkSpeed);
transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle();
// Handle footsteps.

View File

@@ -12,9 +12,9 @@ namespace Content.Server.GameObjects.EntitySystems
{
public class VerbSystem : EntitySystem
{
#pragma warning disable 649
#pragma warning disable 649
[Dependency] private readonly IEntityManager _entityManager;
#pragma warning restore 649
#pragma warning restore 649
public override void Initialize()
{
@@ -90,19 +90,19 @@ namespace Content.Server.GameObjects.EntitySystems
var userEntity = player.AttachedEntity;
var data = new List<VerbsResponseMessage.VerbData>();
var data = new List<VerbsResponseMessage.NetVerbData>();
//Get verbs, component dependent.
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
{
if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity))
continue;
if (VerbUtility.IsVerbInvisible(verb, userEntity, component, out var vis))
var verbData = verb.GetData(userEntity, component);
if (verbData.IsInvisible)
continue;
// TODO: These keys being giant strings is inefficient as hell.
data.Add(new VerbsResponseMessage.VerbData(verb.GetText(userEntity, component),
$"{component.GetType()}:{verb.GetType()}", verb.GetCategory(userEntity, component),
vis == VerbVisibility.Visible));
data.Add(new VerbsResponseMessage.NetVerbData(verbData, $"{component.GetType()}:{verb.GetType()}"));
}
//Get global verbs. Visible for all entities regardless of their components.
@@ -110,14 +110,15 @@ namespace Content.Server.GameObjects.EntitySystems
{
if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity))
continue;
if (VerbUtility.IsVerbInvisible(globalVerb, userEntity, entity, out var vis))
var verbData = globalVerb.GetData(userEntity, entity);
if (verbData.IsInvisible)
continue;
data.Add(new VerbsResponseMessage.VerbData(globalVerb.GetText(userEntity, entity),
globalVerb.GetType().ToString(), globalVerb.GetCategory(userEntity, entity), vis == VerbVisibility.Visible));
data.Add(new VerbsResponseMessage.NetVerbData(verbData, globalVerb.GetType().ToString()));
}
var response = new VerbsResponseMessage(data, req.EntityUid);
var response = new VerbsResponseMessage(data.ToArray(), req.EntityUid);
RaiseNetworkEvent(response, player.ConnectedClient);
}
}

View File

@@ -1,53 +1,64 @@
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Nutrition;
using Content.Server.GameObjects.Components.Observer;
using Content.Server.Players;
using Content.Shared.GameObjects;
using Robust.Server.Console;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
namespace Content.Server.GlobalVerbs
{
[GlobalVerb]
public class ControlMobVerb : GlobalVerb
{
public override string GetText(IEntity user, IEntity target) => "Control Mob";
public override string GetCategory(IEntity user, IEntity target) => "Debug";
public override bool RequireInteractionRange => false;
public override VerbVisibility GetVisibility(IEntity user, IEntity target)
public override void GetData(IEntity user, IEntity target, VerbData data)
{
data.Visibility = VerbVisibility.Invisible;
var groupController = IoCManager.Resolve<IConGroupController>();
if (user == target) return VerbVisibility.Invisible;
if (user == target)
{
return;
}
if (user.TryGetComponent<IActorComponent>(out var player))
{
if (!user.HasComponent<MindComponent>() || !target.HasComponent<MindComponent>())
return VerbVisibility.Invisible;
{
return;
}
if (groupController.CanCommand(player.playerSession, "controlmob"))
return VerbVisibility.Visible;
{
data.Visibility = VerbVisibility.Visible;
data.Text = "Control Mob";
data.CategoryData = VerbCategories.Debug;
}
}
return VerbVisibility.Invisible;
}
public override void Activate(IEntity user, IEntity target)
{
var userMind = user.GetComponent<IActorComponent>().playerSession.ContentData().Mind;
var groupController = IoCManager.Resolve<IConGroupController>();
var player = user.GetComponent<IActorComponent>().playerSession;
if (!groupController.CanCommand(player, "controlmob"))
{
return;
}
var userMind = player.ContentData().Mind;
var targetMind = target.GetComponent<MindComponent>();
var oldEntity = userMind.CurrentEntity;
targetMind.Mind?.TransferTo(null);
userMind.TransferTo(target);
if(oldEntity.HasComponent<GhostComponent>())
if (oldEntity.HasComponent<GhostComponent>())
oldEntity.Delete();
}
}

View File

@@ -14,24 +14,29 @@ namespace Content.Server.GlobalVerbs
[GlobalVerb]
class RejuvenateVerb : GlobalVerb
{
public override string GetText(IEntity user, IEntity target) => "Rejuvenate";
public override string GetCategory(IEntity user, IEntity target) => "Debug";
public override bool RequireInteractionRange => false;
public override VerbVisibility GetVisibility(IEntity user, IEntity target)
public override void GetData(IEntity user, IEntity target, VerbData data)
{
data.Text = "Rejuvenate";
data.CategoryData = VerbCategories.Debug;
data.Visibility = VerbVisibility.Invisible;
var groupController = IoCManager.Resolve<IConGroupController>();
if (user.TryGetComponent<IActorComponent>(out var player))
{
if (!target.HasComponent<DamageableComponent>() && !target.HasComponent<HungerComponent>() && !target.HasComponent<ThirstComponent>())
return VerbVisibility.Invisible;
if (!target.HasComponent<DamageableComponent>() && !target.HasComponent<HungerComponent>() &&
!target.HasComponent<ThirstComponent>())
{
return;
}
if (groupController.CanCommand(player.playerSession, "rejuvenate"))
return VerbVisibility.Visible;
{
data.Visibility = VerbVisibility.Visible;
}
}
return VerbVisibility.Invisible;
}
public override void Activate(IEntity user, IEntity target)
@@ -42,7 +47,6 @@ namespace Content.Server.GlobalVerbs
if (groupController.CanCommand(player.playerSession, "rejuvenate"))
PerformRejuvenate(target);
}
}
public static void PerformRejuvenate(IEntity target)
{

View File

@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Content.Server.Utility;
using Content.Shared.BodySystem;
@@ -41,6 +42,7 @@ namespace Content.Server.BodySystem
{
return;
}
if (actor.playerSession.AttachedEntity.TryGetComponent(out BodyManagerComponent attempt))
{
_userInterface.SetState(PrepareBodyScannerInterfaceState(attempt.Template, attempt.PartDictionary));

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using Content.Server.BodySystem;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.BodySystem;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items;
@@ -42,6 +43,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee
void IAfterAttack.AfterAttack(AfterAttackEventArgs eventArgs)
{
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
if (eventArgs.Attacked == null)
return;
if (eventArgs.Attacked.TryGetComponent<BodySystem.BodyManagerComponent>(out BodySystem.BodyManagerComponent bodyManager))

View File

@@ -26,7 +26,7 @@ namespace Content.Server.Throw
var mapManager = IoCManager.Resolve<IMapManager>();
colComp.CollisionEnabled = true;
colComp.CanCollide = true;
// I can now collide with player, so that i can do damage.
if (!thrownEnt.TryGetComponent(out ThrownItemComponent projComp))
@@ -37,7 +37,7 @@ namespace Content.Server.Throw
colComp.PhysicsShapes.Add(new PhysShapeAabb());
colComp.PhysicsShapes[0].CollisionMask |= (int) (CollisionGroup.MobImpassable | CollisionGroup.Impassable);
colComp.IsScrapingFloor = false;
colComp.Status = BodyStatus.InAir;
}
var angle = new Angle(targetLoc.ToMapPos(mapManager) - sourceLoc.ToMapPos(mapManager));
@@ -58,20 +58,16 @@ namespace Content.Server.Throw
if (!thrownEnt.TryGetComponent(out PhysicsComponent physComp))
physComp = thrownEnt.AddComponent<PhysicsComponent>();
// TODO: Move this into PhysicsSystem, we need an ApplyForce function.
var a = throwForce / (float) Math.Max(0.001, physComp.Mass); // a = f / m
var timing = IoCManager.Resolve<IGameTiming>();
var spd = a / (1f / timing.TickRate); // acceleration is applied in 1 tick instead of 1 second, scale appropriately
var spd = throwForce / (1f / timing.TickRate); // acceleration is applied in 1 tick instead of 1 second, scale appropriately
physComp.LinearVelocity = angle.ToVec() * spd;
physComp.SetController<ThrowController>();
(physComp.Controller as ThrowController)?.StartThrow(angle.ToVec() * spd);
if (throwSourceEnt != null)
if (throwSourceEnt != null && throwSourceEnt.TryGetComponent<PhysicsComponent>(out var physics))
{
var p = throwSourceEnt.GetComponent<PhysicsComponent>();
var playerAccel = 5 * throwForce / (float) Math.Max(0.001, p.Mass);
p.LinearVelocity = Angle.FromDegrees(angle.Degrees + 180).ToVec()
* playerAccel / (1f / timing.TickRate);
const float ThrowFactor = 5.0f; // Break Newton's Third Law for better gameplay
(physics.Controller as MoverController)?.Push(-angle.ToVec(), spd * ThrowFactor / physics.Mass);
}
}
}

View File

@@ -0,0 +1,100 @@
using System;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Interfaces;
using Content.Shared.Physics;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.Server.Utility
{
/// <summary>
/// Convenient methods for checking for various conditions commonly needed
/// for interactions.
/// </summary>
public static class InteractionChecks
{
/// <summary>
/// Default interaction check for targeted attack interaction types.
/// Same as <see cref="SharedInteractionSystem.InRangeUnobstructed"/>, but defaults to allow inside blockers.
/// Validates that attacker is in range of the attacked entity. Additionally shows a popup if
/// validation fails.
/// </summary>
public static bool InRangeUnobstructed(ITargetedAttackEventArgs eventArgs, bool insideBlockerValid = true)
{
if (!EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(eventArgs.User.Transform.MapPosition,
eventArgs.Attacked.Transform.WorldPosition, ignoredEnt: eventArgs.Attacked, insideBlockerValid: insideBlockerValid))
{
var localizationManager = IoCManager.Resolve<ILocalizationManager>();
eventArgs.Attacked.PopupMessage(eventArgs.User, localizationManager.GetString("You can't reach there!"));
return false;
}
return true;
}
/// <summary>
/// Default interaction check for after attack interaction types.
/// Same as <see cref="SharedInteractionSystem.InRangeUnobstructed"/>, but defaults to allow inside blockers.
/// Validates that attacker is in range of the attacked entity, if there is such an entity.
/// If there is no attacked entity, validates that they are in range of the clicked position.
/// Additionally shows a popup if validation fails.
/// </summary>
public static bool InRangeUnobstructed(AfterAttackEventArgs eventArgs, bool insideBlockerValid = true)
{
if (eventArgs.Attacked != null)
{
if (!EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(eventArgs.User.Transform.MapPosition,
eventArgs.Attacked.Transform.WorldPosition, ignoredEnt: eventArgs.Attacked, insideBlockerValid: insideBlockerValid))
{
var localizationManager = IoCManager.Resolve<ILocalizationManager>();
eventArgs.Attacked.PopupMessage(eventArgs.User, localizationManager.GetString("You can't reach there!"));
return false;
}
}
else
{
var mapManager = IoCManager.Resolve<IMapManager>();
if (!EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(eventArgs.User.Transform.MapPosition,
eventArgs.ClickLocation.ToMapPos(mapManager), ignoredEnt: eventArgs.User, insideBlockerValid: insideBlockerValid))
{
var localizationManager = IoCManager.Resolve<ILocalizationManager>();
eventArgs.User.PopupMessage(eventArgs.User, localizationManager.GetString("You can't reach there!"));
return false;
}
}
return true;
}
/// <summary>
/// Convenient static alternative to <see cref="SharedInteractionSystem.InRangeUnobstructed"/>, which also
/// shows a popup message if not in range.
/// </summary>
public static bool InRangeUnobstructed(IEntity user, Vector2 targetWorldCoords,
float range = SharedInteractionSystem.InteractionRange,
int collisionMask = (int) CollisionGroup.Impassable, IEntity ignoredEnt = null,
bool insideBlockerValid = false)
{
var interactionSystem = EntitySystem.Get<SharedInteractionSystem>();
if (!interactionSystem.InRangeUnobstructed(user.Transform.MapPosition, targetWorldCoords, range, collisionMask,
ignoredEnt, insideBlockerValid))
{
var localizationManager = IoCManager.Resolve<ILocalizationManager>();
user.PopupMessage(user, localizationManager.GetString("You can't reach there!"));
return false;
}
return true;
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using Content.Shared.BodySystem;
using Robust.Shared.Audio.Midi;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
@@ -9,6 +10,10 @@ namespace Content.Shared.GameObjects.Components.Instruments
{
public override string Name => "Instrument";
public override uint? NetID => ContentNetIDs.INSTRUMENTS;
public virtual void Update(float delta)
{
}
}
@@ -20,19 +25,39 @@ namespace Content.Shared.GameObjects.Components.Instruments
{
}
/// <summary>
/// This message is sent to the client to start the synth.
/// </summary>
[Serializable, NetSerializable]
public class InstrumentStartMidiMessage : ComponentMessage
{
}
/// <summary>
/// This message carries a MidiEvent to be played on clients.
/// </summary>
[Serializable, NetSerializable]
public class InstrumentMidiEventMessage : ComponentMessage
{
public MidiEvent MidiEvent;
public double Timestamp;
public MidiEvent[] MidiEvent;
public InstrumentMidiEventMessage(MidiEvent midiEvent, double timestamp)
public InstrumentMidiEventMessage(MidiEvent[] midiEvent)
{
MidiEvent = midiEvent;
Timestamp = timestamp;
}
}
[Serializable, NetSerializable]
public class InstrumentState : ComponentState
{
public bool Playing { get; }
public uint SequencerTick { get; }
public InstrumentState(bool playing, uint sequencerTick = 0) : base(ContentNetIDs.INSTRUMENTS)
{
Playing = playing;
SequencerTick = sequencerTick;
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Shared.GameObjects.EntitySystemMessages
{
@@ -21,29 +22,33 @@ namespace Content.Shared.GameObjects.EntitySystemMessages
[Serializable, NetSerializable]
public class VerbsResponseMessage : EntitySystemMessage
{
public readonly List<VerbData> Verbs;
public readonly NetVerbData[] Verbs;
public readonly EntityUid Entity;
public VerbsResponseMessage(List<VerbData> verbs, EntityUid entity)
public VerbsResponseMessage(NetVerbData[] verbs, EntityUid entity)
{
Verbs = verbs;
Entity = entity;
}
[Serializable, NetSerializable]
public readonly struct VerbData
public readonly struct NetVerbData
{
public readonly string Text;
public readonly string Key;
public readonly string Category;
public readonly SpriteSpecifier Icon;
public readonly SpriteSpecifier CategoryIcon;
public readonly bool Available;
public VerbData(string text, string key, string category, bool available)
public NetVerbData(VerbData data, string key)
{
Text = text;
Text = data.Text;
Key = key;
Category = category;
Available = available;
Category = data.Category;
CategoryIcon = data.CategoryIcon;
Icon = data.Icon;
Available = data.Visibility == VerbVisibility.Visible;
}
}
}

View File

@@ -3,7 +3,7 @@ using Content.Shared.GameObjects.Components.Mobs;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
namespace Content.Shared.GameObjects.EntitySystems
{
@@ -30,8 +30,7 @@ namespace Content.Shared.GameObjects.EntitySystems
return false;
}
return IoCManager.Resolve<IEntitySystemManager>()
.GetEntitySystem<SharedInteractionSystem>()
return EntitySystem.Get<SharedInteractionSystem>()
.InRangeUnobstructed(examiner.Transform.MapPosition, examined.Transform.MapPosition.Position,
ExamineRange, predicate: entity => entity == examiner || entity == examined, insideBlockerValid:true);
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Content.Shared.Physics;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Systems;
@@ -36,7 +37,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <param name="range">maximum distance between the two sets of coordinates.</param>
/// <param name="collisionMask">the mask to check for collisions</param>
/// <param name="predicate">.</param>
/// <param name="mapManager">Map manager containing the two GridIds.</param>
/// <param name="insideBlockerValid">if coordinates inside obstructions count as obstructed or not</param>
/// <returns>True if the two points are within a given range without being obstructed.</returns>
public bool InRangeUnobstructed(MapCoordinates coords, Vector2 otherCoords, float range = InteractionRange,
@@ -48,8 +48,8 @@ namespace Content.Server.GameObjects.EntitySystems
if (range > 0f && !(dir.LengthSquared <= range * range)) return false;
var ray = new CollisionRay(coords.Position, dir.Normalized, collisionMask);
var rayResults = _physicsManager.IntersectRayWithPredicate(coords.MapId, ray, dir.Length, predicate, true);
if(!rayResults.DidHitObject || (insideBlockerValid && rayResults.DidHitObject && (rayResults.HitPos - otherCoords).Length < 1f))
var rayResults = _physicsManager.IntersectRayWithPredicate(coords.MapId, ray, dir.Length, predicate).ToList();
if(rayResults.Count == 0 || (insideBlockerValid && rayResults.Count > 0 && (rayResults[0].HitPos - otherCoords).Length < 1f))
{
if (_mapManager.TryFindGridAt(coords, out var mapGrid) && mapGrid != null)
@@ -77,7 +77,6 @@ namespace Content.Server.GameObjects.EntitySystems
/// <param name="range">maximum distance between the two sets of coordinates.</param>
/// <param name="collisionMask">the mask to check for collisions</param>
/// <param name="ignoredEnt">the entity to be ignored when checking for collisions.</param>
/// <param name="mapManager">Map manager containing the two GridIds.</param>
/// <param name="insideBlockerValid">if coordinates inside obstructions count as obstructed or not</param>
/// <returns>True if the two points are within a given range without being obstructed.</returns>
public bool InRangeUnobstructed(MapCoordinates coords, Vector2 otherCoords, float range = InteractionRange,

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