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

This commit is contained in:
zumorica
2020-05-11 12:24:43 +02:00
114 changed files with 3532 additions and 531 deletions

View File

@@ -1,6 +1,6 @@
{ {
"recommendations": [ "recommendations": [
"ms-vscode.csharp", "ms-dotnettools.csharp",
"editorconfig.editorconfig" "editorconfig.editorconfig"
] ]
} }

View File

@@ -6,7 +6,7 @@ namespace Content.Benchmarks
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
BenchmarkRunner.Run<ComponentManagerGetAllComponents>(); BenchmarkRunner.Run<StereoToMonoBenchmark>();
} }
} }
} }

View File

@@ -0,0 +1,70 @@
using System.Runtime.Intrinsics.X86;
using BenchmarkDotNet.Attributes;
namespace Content.Benchmarks
{
public class StereoToMonoBenchmark
{
[Params(128, 256, 512)]
public int N { get; set; }
private short[] _input;
private short[] _output;
[GlobalSetup]
public void Setup()
{
_input = new short[N * 2];
_output = new short[N];
}
[Benchmark]
public void BenchSimple()
{
var l = N;
for (var j = 0; j < l; j++)
{
var k = j + l;
_output[j] = (short) ((_input[k] + _input[j]) / 2);
}
}
[Benchmark]
public unsafe void BenchSse()
{
var l = N;
fixed (short* iPtr = _input)
fixed (short* oPtr = _output)
{
for (var j = 0; j < l; j += 8)
{
var k = j + l;
var jV = Sse2.ShiftRightArithmetic(Sse2.LoadVector128(iPtr + j), 1);
var kV = Sse2.ShiftRightArithmetic(Sse2.LoadVector128(iPtr + k), 1);
Sse2.Store(j + oPtr, Sse2.Add(jV, kV));
}
}
}
[Benchmark]
public unsafe void BenchAvx2()
{
var l = N;
fixed (short* iPtr = _input)
fixed (short* oPtr = _output)
{
for (var j = 0; j < l; j += 16)
{
var k = j + l;
var jV = Avx2.ShiftRightArithmetic(Avx.LoadVector256(iPtr + j), 1);
var kV = Avx2.ShiftRightArithmetic(Avx.LoadVector256(iPtr + k), 1);
Avx.Store(j + oPtr, Avx2.Add(jV, kV));
}
}
}
}
}

View File

@@ -19,6 +19,12 @@ namespace Content.Client.Chat
{ {
internal sealed class ChatManager : IChatManager internal sealed class ChatManager : IChatManager
{ {
private struct SpeechBubbleData
{
public string Message;
public SpeechBubble.SpeechType Type;
}
/// <summary> /// <summary>
/// The max amount of chars allowed to fit in a single speech bubble. /// The max amount of chars allowed to fit in a single speech bubble.
/// </summary> /// </summary>
@@ -117,7 +123,7 @@ namespace Content.Client.Chat
var msg = queueData.MessageQueue.Dequeue(); var msg = queueData.MessageQueue.Dequeue();
queueData.TimeLeft += BubbleDelayBase + msg.Length * BubbleDelayFactor; queueData.TimeLeft += BubbleDelayBase + msg.Message.Length * BubbleDelayFactor;
// We keep the queue around while it has 0 items. This allows us to keep the timer. // We keep the queue around while it has 0 items. This allows us to keep the timer.
// When the timer hits 0 and there's no messages left, THEN we can clear it up. // When the timer hits 0 and there's no messages left, THEN we can clear it up.
@@ -211,19 +217,19 @@ namespace Content.Client.Chat
case OOCAlias: case OOCAlias:
{ {
var conInput = text.Substring(1); var conInput = text.Substring(1);
_console.ProcessCommand($"ooc \"{conInput}\""); _console.ProcessCommand($"ooc \"{CommandParsing.Escape(conInput)}\"");
break; break;
} }
case MeAlias: case MeAlias:
{ {
var conInput = text.Substring(1); var conInput = text.Substring(1);
_console.ProcessCommand($"me \"{conInput}\""); _console.ProcessCommand($"me \"{CommandParsing.Escape(conInput)}\"");
break; break;
} }
default: default:
{ {
var conInput = _currentChatBox.DefaultChatFormat != null var conInput = _currentChatBox.DefaultChatFormat != null
? string.Format(_currentChatBox.DefaultChatFormat, text) ? string.Format(_currentChatBox.DefaultChatFormat, CommandParsing.Escape(text))
: text; : text;
_console.ProcessCommand(conInput); _console.ProcessCommand(conInput);
break; break;
@@ -291,13 +297,23 @@ namespace Content.Client.Chat
WriteChatMessage(storedMessage); WriteChatMessage(storedMessage);
// Local messages that have an entity attached get a speech bubble. // Local messages that have an entity attached get a speech bubble.
if ((msg.Channel == ChatChannel.Local || msg.Channel == ChatChannel.Dead) && msg.SenderEntity != default) if (msg.SenderEntity == default)
return;
switch (msg.Channel)
{ {
AddSpeechBubble(msg); case ChatChannel.Local:
case ChatChannel.Dead:
AddSpeechBubble(msg, SpeechBubble.SpeechType.Say);
break;
case ChatChannel.Emotes:
AddSpeechBubble(msg, SpeechBubble.SpeechType.Emote);
break;
} }
} }
private void AddSpeechBubble(MsgChatMessage msg) private void AddSpeechBubble(MsgChatMessage msg, SpeechBubble.SpeechType speechType)
{ {
if (!_entityManager.TryGetEntity(msg.SenderEntity, out var entity)) if (!_entityManager.TryGetEntity(msg.SenderEntity, out var entity))
{ {
@@ -305,8 +321,18 @@ namespace Content.Client.Chat
return; return;
} }
var messages = SplitMessage(msg.Message);
foreach (var message in messages)
{
EnqueueSpeechBubble(entity, message, speechType);
}
}
private List<string> SplitMessage(string msg)
{
// Split message into words separated by spaces. // Split message into words separated by spaces.
var words = msg.Message.Split(' '); var words = msg.Split(' ');
var messages = new List<string>(); var messages = new List<string>();
var currentBuffer = new List<string>(); var currentBuffer = new List<string>();
@@ -346,13 +372,10 @@ namespace Content.Client.Chat
messages.Add(string.Join(" ", currentBuffer)); messages.Add(string.Join(" ", currentBuffer));
} }
foreach (var message in messages) return messages;
{
EnqueueSpeechBubble(entity, message);
}
} }
private void EnqueueSpeechBubble(IEntity entity, string contents) private void EnqueueSpeechBubble(IEntity entity, string contents, SpeechBubble.SpeechType speechType)
{ {
if (!_queuedSpeechBubbles.TryGetValue(entity.Uid, out var queueData)) if (!_queuedSpeechBubbles.TryGetValue(entity.Uid, out var queueData))
{ {
@@ -360,12 +383,16 @@ namespace Content.Client.Chat
_queuedSpeechBubbles.Add(entity.Uid, queueData); _queuedSpeechBubbles.Add(entity.Uid, queueData);
} }
queueData.MessageQueue.Enqueue(contents); queueData.MessageQueue.Enqueue(new SpeechBubbleData
{
Message = contents,
Type = speechType,
});
} }
private void CreateSpeechBubble(IEntity entity, string contents) private void CreateSpeechBubble(IEntity entity, SpeechBubbleData speechData)
{ {
var bubble = new SpeechBubble(contents, entity, _eyeManager, this); var bubble = SpeechBubble.CreateSpeechBubble(speechData.Type, speechData.Message, entity, _eyeManager, this);
if (_activeSpeechBubbles.TryGetValue(entity.Uid, out var existing)) if (_activeSpeechBubbles.TryGetValue(entity.Uid, out var existing))
{ {
@@ -405,7 +432,7 @@ namespace Content.Client.Chat
/// </summary> /// </summary>
public float TimeLeft { get; set; } public float TimeLeft { get; set; }
public Queue<string> MessageQueue { get; } = new Queue<string>(); public Queue<SpeechBubbleData> MessageQueue { get; } = new Queue<SpeechBubbleData>();
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using Content.Client.Interfaces.Chat; using System;
using Content.Client.Interfaces.Chat;
using Robust.Client.Interfaces.Graphics.ClientEye; using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
@@ -9,8 +10,14 @@ using Robust.Shared.Timing;
namespace Content.Client.Chat namespace Content.Client.Chat
{ {
public class SpeechBubble : Control public abstract class SpeechBubble : Control
{ {
public enum SpeechType
{
Emote,
Say
}
/// <summary> /// <summary>
/// The total time a speech bubble stays on screen. /// The total time a speech bubble stays on screen.
/// </summary> /// </summary>
@@ -38,6 +45,21 @@ namespace Content.Client.Chat
public float ContentHeight { get; private set; } public float ContentHeight { get; private set; }
public static SpeechBubble CreateSpeechBubble(SpeechType type, string text, IEntity senderEntity, IEyeManager eyeManager, IChatManager chatManager)
{
switch (type)
{
case SpeechType.Emote:
return new EmoteSpeechBubble(text, senderEntity, eyeManager, chatManager);
case SpeechType.Say:
return new SaySpeechBubble(text, senderEntity, eyeManager, chatManager);
default:
throw new ArgumentOutOfRangeException();
}
}
public SpeechBubble(string text, IEntity senderEntity, IEyeManager eyeManager, IChatManager chatManager) public SpeechBubble(string text, IEntity senderEntity, IEyeManager eyeManager, IChatManager chatManager)
{ {
_chatManager = chatManager; _chatManager = chatManager;
@@ -47,27 +69,18 @@ namespace Content.Client.Chat
// Use text clipping so new messages don't overlap old ones being pushed up. // Use text clipping so new messages don't overlap old ones being pushed up.
RectClipContent = true; RectClipContent = true;
var label = new RichTextLabel var bubble = BuildBubble(text);
{
MaxWidth = 256,
};
label.SetMessage(text);
var panel = new PanelContainer AddChild(bubble);
{
StyleClasses = { "tooltipBox" },
Children = { label },
ModulateSelfOverride = Color.White.WithAlpha(0.75f)
};
AddChild(panel);
ForceRunStyleUpdate(); ForceRunStyleUpdate();
ContentHeight = panel.CombinedMinimumSize.Y; ContentHeight = bubble.CombinedMinimumSize.Y;
_verticalOffsetAchieved = -ContentHeight; _verticalOffsetAchieved = -ContentHeight;
} }
protected abstract Control BuildBubble(string text);
protected override Vector2 CalculateMinimumSize() protected override Vector2 CalculateMinimumSize()
{ {
return (base.CalculateMinimumSize().X, 0); return (base.CalculateMinimumSize().X, 0);
@@ -134,4 +147,57 @@ namespace Content.Client.Chat
} }
} }
} }
public class EmoteSpeechBubble : SpeechBubble
{
public EmoteSpeechBubble(string text, IEntity senderEntity, IEyeManager eyeManager, IChatManager chatManager)
: base(text, senderEntity, eyeManager, chatManager)
{
}
protected override Control BuildBubble(string text)
{
var label = new RichTextLabel
{
MaxWidth = 256,
};
label.SetMessage(text);
var panel = new PanelContainer
{
StyleClasses = { "speechBox", "emoteBox" },
Children = { label },
ModulateSelfOverride = Color.White.WithAlpha(0.75f)
};
return panel;
}
}
public class SaySpeechBubble : SpeechBubble
{
public SaySpeechBubble(string text, IEntity senderEntity, IEyeManager eyeManager, IChatManager chatManager)
: base(text, senderEntity, eyeManager, chatManager)
{
}
protected override Control BuildBubble(string text)
{
var label = new RichTextLabel
{
MaxWidth = 256,
};
label.SetMessage(text);
var panel = new PanelContainer
{
StyleClasses = { "speechBox", "sayBox" },
Children = { label },
ModulateSelfOverride = Color.White.WithAlpha(0.75f)
};
return panel;
}
}
} }

View File

@@ -31,6 +31,7 @@ namespace Content.Client
IoCManager.Register<IClientPreferencesManager, ClientPreferencesManager>(); IoCManager.Register<IClientPreferencesManager, ClientPreferencesManager>();
IoCManager.Register<IItemSlotManager, ItemSlotManager>(); IoCManager.Register<IItemSlotManager, ItemSlotManager>();
IoCManager.Register<IStylesheetManager, StylesheetManager>(); IoCManager.Register<IStylesheetManager, StylesheetManager>();
IoCManager.Register<IScreenshotHook, ScreenshotHook>();
} }
} }
} }

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Client.Interfaces; using Content.Client.Interfaces;
using Content.Client.UserInterface.Stylesheets;
using Content.Shared; using Content.Shared;
using Robust.Client.Interfaces.Console; using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Graphics.ClientEye; using Robust.Client.Interfaces.Graphics.ClientEye;
@@ -57,7 +58,11 @@ namespace Content.Client
public void PopupMessage(ScreenCoordinates coordinates, string message) public void PopupMessage(ScreenCoordinates coordinates, string message)
{ {
var label = new PopupLabel {Text = message}; var label = new PopupLabel
{
Text = message,
StyleClasses = { StyleNano.StyleClassPopupMessage },
};
var minimumSize = label.CombinedMinimumSize; var minimumSize = label.CombinedMinimumSize;
LayoutContainer.SetPosition(label, label.InitialPos = coordinates.Position - minimumSize / 2); LayoutContainer.SetPosition(label, label.InitialPos = coordinates.Position - minimumSize / 2);
_userInterfaceManager.PopupRoot.AddChild(label); _userInterfaceManager.PopupRoot.AddChild(label);

View File

@@ -12,9 +12,11 @@ using Content.Client.UserInterface.Stylesheets;
using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Cargo; using Content.Shared.GameObjects.Components.Cargo;
using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.Components.Chemistry;
using Content.Shared.GameObjects.Components.Gravity;
using Content.Shared.GameObjects.Components.Markers; using Content.Shared.GameObjects.Components.Markers;
using Content.Shared.GameObjects.Components.Research; using Content.Shared.GameObjects.Components.Research;
using Content.Shared.GameObjects.Components.VendingMachines; using Content.Shared.GameObjects.Components.VendingMachines;
using Content.Shared.Kitchen;
using Robust.Client; using Robust.Client;
using Robust.Client.Interfaces; using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Graphics.Overlays; using Robust.Client.Interfaces.Graphics.Overlays;
@@ -142,7 +144,7 @@ namespace Content.Client
"Mop", "Mop",
"Bucket", "Bucket",
"Puddle", "Puddle",
"CanSpill", "CanSpill"
}; };
foreach (var ignoreName in registerIgnore) foreach (var ignoreName in registerIgnore)
@@ -160,6 +162,8 @@ namespace Content.Client
factory.Register<SharedWiresComponent>(); factory.Register<SharedWiresComponent>();
factory.Register<SharedCargoConsoleComponent>(); factory.Register<SharedCargoConsoleComponent>();
factory.Register<SharedReagentDispenserComponent>(); factory.Register<SharedReagentDispenserComponent>();
factory.Register<SharedMicrowaveComponent>();
factory.Register<SharedGravityGeneratorComponent>();
prototypes.RegisterIgnore("material"); prototypes.RegisterIgnore("material");
prototypes.RegisterIgnore("reaction"); //Chemical reactions only needed by server. Reactions checks are server-side. prototypes.RegisterIgnore("reaction"); //Chemical reactions only needed by server. Reactions checks are server-side.
@@ -178,6 +182,7 @@ namespace Content.Client
IoCManager.Resolve<IParallaxManager>().LoadParallax(); IoCManager.Resolve<IParallaxManager>().LoadParallax();
IoCManager.Resolve<IBaseClient>().PlayerJoinedServer += SubscribePlayerAttachmentEvents; IoCManager.Resolve<IBaseClient>().PlayerJoinedServer += SubscribePlayerAttachmentEvents;
IoCManager.Resolve<IStylesheetManager>().Initialize(); IoCManager.Resolve<IStylesheetManager>().Initialize();
IoCManager.Resolve<IScreenshotHook>().Initialize();
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);

View File

@@ -0,0 +1,104 @@
using System;
using Content.Shared.GameObjects.Components.Gravity;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Gravity
{
public class GravityGeneratorBoundUserInterface: BoundUserInterface
{
private GravityGeneratorWindow _window;
public bool IsOn;
public GravityGeneratorBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base (owner, uiKey)
{
SendMessage(new SharedGravityGeneratorComponent.GeneratorStatusRequestMessage());
}
protected override void Open()
{
base.Open();
IsOn = false;
_window = new GravityGeneratorWindow(this);
_window.Switch.OnPressed += (args) =>
{
SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(!IsOn));
SendMessage(new SharedGravityGeneratorComponent.GeneratorStatusRequestMessage());
};
_window.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (SharedGravityGeneratorComponent.GeneratorState) state;
IsOn = castState.On;
_window.UpdateButton();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
}
public class GravityGeneratorWindow : SS14Window
{
public Label Status;
public Button Switch;
public GravityGeneratorBoundUserInterface Owner;
public GravityGeneratorWindow(GravityGeneratorBoundUserInterface gravityGeneratorInterface = null)
{
IoCManager.InjectDependencies(this);
Owner = gravityGeneratorInterface;
Title = Loc.GetString("Gravity Generator Control");
var vBox = new VBoxContainer
{
CustomMinimumSize = new Vector2(250, 100)
};
Status = new Label
{
Text = Loc.GetString("Current Status: " + (Owner.IsOn ? "On" : "Off")),
FontColorOverride = Owner.IsOn ? Color.ForestGreen : Color.Red
};
Switch = new Button
{
Text = Loc.GetString(Owner.IsOn ? "Turn Off" : "Turn On"),
TextAlign = Label.AlignMode.Center,
CustomMinimumSize = new Vector2(150, 60)
};
vBox.AddChild(Status);
vBox.AddChild(Switch);
Contents.AddChild(vBox);
}
public void UpdateButton()
{
Status.Text = Loc.GetString("Current Status: " + (Owner.IsOn ? "On" : "Off"));
Status.FontColorOverride = Owner.IsOn ? Color.ForestGreen : Color.Red;
Switch.Text = Loc.GetString(Owner.IsOn ? "Turn Off" : "Turn On");
}
}
}

View File

@@ -114,13 +114,13 @@ namespace Content.Client.GameObjects
} }
} }
protected override void HandleInventoryKeybind(BaseButton.ButtonEventArgs args, Slots slot) protected override void HandleInventoryKeybind(GUIBoundKeyEventArgs args, Slots slot)
{ {
if (!_inventoryButtons.TryGetValue(slot, out var buttons)) if (!_inventoryButtons.TryGetValue(slot, out var buttons))
return; return;
if (!Owner.TryGetSlot(slot, out var item)) if (!Owner.TryGetSlot(slot, out var item))
return; return;
if (_itemSlotManager.OnButtonPressed(args.Event, item)) if (_itemSlotManager.OnButtonPressed(args, item))
return; return;
base.HandleInventoryKeybind(args, slot); base.HandleInventoryKeybind(args, slot);

View File

@@ -2,8 +2,9 @@
using Content.Client.UserInterface; using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.Input; using Content.Shared.Input;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
@@ -62,39 +63,35 @@ namespace Content.Client.GameObjects
{ {
} }
protected virtual void HandleInventoryKeybind(BaseButton.ButtonEventArgs args, EquipmentSlotDefines.Slots slot) protected virtual void HandleInventoryKeybind(GUIBoundKeyEventArgs args, EquipmentSlotDefines.Slots slot)
{ {
if (args.Event.CanFocus) if (args.Function == EngineKeyFunctions.UIClick)
{ {
UseItemOnInventory(args, slot); UseItemOnInventory(slot);
} }
} }
protected void AddToInventory(BaseButton.ButtonEventArgs args, EquipmentSlotDefines.Slots slot) protected void AddToInventory(GUIBoundKeyEventArgs args, EquipmentSlotDefines.Slots slot)
{ {
if (!args.Event.CanFocus) if (args.Function != EngineKeyFunctions.UIClick)
{ {
return; return;
} }
args.Button.Pressed = false;
Owner.SendEquipMessage(slot); Owner.SendEquipMessage(slot);
} }
protected void UseItemOnInventory(BaseButton.ButtonEventArgs args, EquipmentSlotDefines.Slots slot) protected void UseItemOnInventory(EquipmentSlotDefines.Slots slot)
{ {
args.Button.Pressed = false;
Owner.SendUseMessage(slot); Owner.SendUseMessage(slot);
} }
protected void OpenStorage(BaseButton.ButtonEventArgs args, EquipmentSlotDefines.Slots slot) protected void OpenStorage(GUIBoundKeyEventArgs args, EquipmentSlotDefines.Slots slot)
{ {
if (!args.Event.CanFocus && args.Event.Function != ContentKeyFunctions.ActivateItemInWorld) if (args.Function != EngineKeyFunctions.UIClick && args.Function != ContentKeyFunctions.ActivateItemInWorld)
{ {
return; return;
} }
args.Button.Pressed = false;
Owner.SendOpenStorageUIMessage(slot); Owner.SendOpenStorageUIMessage(slot);
} }

View File

@@ -0,0 +1,119 @@
using Robust.Client.GameObjects.Components.UserInterface;
using Content.Shared.Kitchen;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Content.Shared.Chemistry;
using Robust.Shared.GameObjects;
using System.Collections.Generic;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.GameObjects.Components.Kitchen
{
public class MicrowaveBoundUserInterface : BoundUserInterface
{
#pragma warning disable 649
[Dependency] private readonly IEntityManager _entityManager;
[Dependency] private readonly IPrototypeManager _prototypeManager;
#pragma warning restore 649
private MicrowaveMenu _menu;
private Dictionary<int, EntityUid> _solids = new Dictionary<int, EntityUid>();
private Dictionary<int, Solution.ReagentQuantity> _reagents =new Dictionary<int, Solution.ReagentQuantity>();
public MicrowaveBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner,uiKey)
{
}
protected override void Open()
{
base.Open();
_menu = new MicrowaveMenu(this);
_menu.OpenCentered();
_menu.OnClose += Close;
_menu.StartButton.OnPressed += args => SendMessage(new SharedMicrowaveComponent.MicrowaveStartCookMessage());
_menu.EjectButton.OnPressed += args => SendMessage(new SharedMicrowaveComponent.MicrowaveEjectMessage());
_menu.IngredientsList.OnItemSelected += args =>
{
SendMessage(new SharedMicrowaveComponent.MicrowaveEjectSolidIndexedMessage(_solids[args.ItemIndex]));
};
_menu.IngredientsListReagents.OnItemSelected += args =>
{
SendMessage(
new SharedMicrowaveComponent.MicrowaveVaporizeReagentIndexedMessage(_reagents[args.ItemIndex]));
};
_menu.OnCookTimeSelected += args =>
{
var actualButton = args.Button as Button;
var newTime = (uint) int.Parse(actualButton.Text);
_menu.VisualCookTime = newTime;
SendMessage(new SharedMicrowaveComponent.MicrowaveSelectCookTimeMessage(newTime));
};
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
{
return;
}
_solids?.Clear();
_menu?.Dispose();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (!(state is MicrowaveUpdateUserInterfaceState cstate))
{
return;
}
RefreshContentsDisplay(cstate.ReagentsReagents, cstate.ContainedSolids);
}
private void RefreshContentsDisplay(IReadOnlyList<Solution.ReagentQuantity> reagents, List<EntityUid> solids)
{
_reagents.Clear();
_menu.IngredientsListReagents.Clear();
foreach (var reagent in reagents)
{
_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype proto);
var reagentAdded = _menu.IngredientsListReagents.AddItem($"{reagent.Quantity} {proto.Name}");
var reagentIndex = _menu.IngredientsListReagents.IndexOf(reagentAdded);
_reagents.Add(reagentIndex, reagent);
}
_solids.Clear();
_menu.IngredientsList.Clear();
foreach (var entityID in solids)
{
var entity = _entityManager.GetEntity(entityID);
if (entity.TryGetComponent(out IconComponent icon))
{
var solidItem = _menu.IngredientsList.AddItem(entity.Name, icon.Icon.Default);
var solidIndex = _menu.IngredientsList.IndexOf(solidItem);
_solids.Add(solidIndex, entityID);
}
}
}
}
}

View File

@@ -0,0 +1,206 @@
using System;
using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Kitchen
{
public class MicrowaveMenu : SS14Window
{
protected override Vector2? CustomSize => (512, 256);
private MicrowaveBoundUserInterface Owner { get; set; }
public event Action<BaseButton.ButtonEventArgs> OnCookTimeSelected;
public uint VisualCookTime = 1;
public Button StartButton { get;}
public Button EjectButton { get;}
public PanelContainer TimerFacePlate { get; }
public ButtonGroup CookTimeButtonGroup { get; }
private VBoxContainer CookTimeButtonVbox { get; }
public ItemList IngredientsList { get;}
public ItemList IngredientsListReagents { get; }
private Label _cookTimeInfoLabel { get; }
public MicrowaveMenu(MicrowaveBoundUserInterface owner = null)
{
Owner = owner;
Title = Loc.GetString("Microwave");
var hSplit = new HBoxContainer
{
SizeFlagsHorizontal = SizeFlags.Fill,
SizeFlagsVertical = SizeFlags.Fill
};
IngredientsListReagents = new ItemList
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
SelectMode = ItemList.ItemListSelectMode.Button,
SizeFlagsStretchRatio = 2,
CustomMinimumSize = (100,128)
};
IngredientsList = new ItemList
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
SelectMode = ItemList.ItemListSelectMode.Button,
SizeFlagsStretchRatio = 2,
CustomMinimumSize = (100,128)
};
hSplit.AddChild(IngredientsListReagents);
//Padding between the lists.
hSplit.AddChild(new Control
{
CustomMinimumSize = (0,5),
});
hSplit.AddChild(IngredientsList);
var vSplit = new VBoxContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
};
hSplit.AddChild(vSplit);
var buttonGridContainer = new VBoxContainer
{
Align = BoxContainer.AlignMode.Center,
SizeFlagsStretchRatio = 3
};
StartButton = new Button
{
Text = Loc.GetString("Start"),
TextAlign = Label.AlignMode.Center,
};
EjectButton = new Button
{
Text = Loc.GetString("Eject All Contents"),
ToolTip = Loc.GetString("This vaporizes all reagents, but ejects any solids."),
TextAlign = Label.AlignMode.Center,
};
buttonGridContainer.AddChild(StartButton);
buttonGridContainer.AddChild(EjectButton);
vSplit.AddChild(buttonGridContainer);
//Padding
vSplit.AddChild(new Control
{
CustomMinimumSize = (0, 15),
SizeFlagsVertical = SizeFlags.Fill,
});
CookTimeButtonGroup = new ButtonGroup();
CookTimeButtonVbox = new VBoxContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
Align = BoxContainer.AlignMode.Center,
};
var index = 0;
for (var i = 0; i <= 12; i++)
{
var newButton = new Button
{
Text = (index <= 0 ? 1 : index).ToString(),
TextAlign = Label.AlignMode.Center,
Group = CookTimeButtonGroup,
};
CookTimeButtonVbox.AddChild(newButton);
newButton.OnPressed += args =>
{
OnCookTimeSelected?.Invoke(args);
_cookTimeInfoLabel.Text = $"{Loc.GetString("COOK TIME")}: {VisualCookTime}";
};
index+=5;
}
var cookTimeOneSecondButton = (Button)CookTimeButtonVbox.GetChild(0);
cookTimeOneSecondButton.Pressed = true;
_cookTimeInfoLabel = new Label
{
Text = Loc.GetString($"COOK TIME: {VisualCookTime}"),
Align = Label.AlignMode.Center,
Modulate = Color.White,
SizeFlagsVertical = SizeFlags.ShrinkCenter
};
var innerTimerPanel = new PanelContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
ModulateSelfOverride = Color.Red,
CustomMinimumSize = (100, 128),
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.Black.WithAlpha(0.5f)},
Children =
{
new VBoxContainer
{
Children =
{
new PanelContainer
{
PanelOverride = new StyleBoxFlat(){BackgroundColor = Color.Gray.WithAlpha(0.2f)},
Children =
{
_cookTimeInfoLabel
}
},
new ScrollContainer()
{
SizeFlagsVertical = SizeFlags.FillExpand,
Children =
{
CookTimeButtonVbox,
}
},
}
}
}
};
TimerFacePlate = new PanelContainer()
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
innerTimerPanel
},
};
vSplit.AddChild(TimerFacePlate);
Contents.AddChild(hSplit);
}
}
}

View File

@@ -0,0 +1,67 @@
using Content.Client.GameObjects.Components.Sound;
using Content.Shared.GameObjects.Components.Power;
using Content.Shared.GameObjects.Components.Sound;
using Content.Shared.Kitchen;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Audio;
using Robust.Shared.Log;
namespace Content.Client.GameObjects.Components.Kitchen
{
public sealed class MicrowaveVisualizer : AppearanceVisualizer
{
private SoundComponent _soundComponent;
private const string MicrowaveSoundLoop = "/Audio/machines/microwave_loop.ogg";
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
_soundComponent ??= component.Owner.GetComponent<SoundComponent>();
if (!component.TryGetData(PowerDeviceVisuals.VisualState, out MicrowaveVisualState state))
{
state = MicrowaveVisualState.Idle;
}
switch (state)
{
case MicrowaveVisualState.Idle:
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_unlit");
_soundComponent.StopAllSounds();
break;
case MicrowaveVisualState.Cooking:
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_running_unlit");
var audioParams = AudioParams.Default;
audioParams.Loop = true;
var schedSound = new ScheduledSound();
schedSound.Filename = MicrowaveSoundLoop;
schedSound.AudioParams = audioParams;
_soundComponent.AddScheduledSound(schedSound);
break;
default:
Logger.Debug($"Something terrible happened in {this}");
break;
}
var glowingPartsVisible = !(component.TryGetData(PowerDeviceVisuals.Powered, out bool powered) && !powered);
sprite.LayerSetVisible(MicrowaveVisualizerLayers.BaseUnlit, glowingPartsVisible);
}
private enum MicrowaveVisualizerLayers
{
Base,
BaseUnlit
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Content.Client.GameObjects.Components.Sound
[RegisterComponent] [RegisterComponent]
public class SoundComponent : SharedSoundComponent public class SoundComponent : SharedSoundComponent
{ {
private readonly List<ScheduledSound> _schedules = new List<ScheduledSound>(); private readonly Dictionary<ScheduledSound, IPlayingAudioStream> _audioStreams = new Dictionary<ScheduledSound, IPlayingAudioStream>();
private AudioSystem _audioSystem; private AudioSystem _audioSystem;
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IRobustRandom _random; [Dependency] private readonly IRobustRandom _random;
@@ -23,26 +23,27 @@ namespace Content.Client.GameObjects.Components.Sound
public override void StopAllSounds() public override void StopAllSounds()
{ {
foreach (var schedule in _schedules) foreach (var kvp in _audioStreams)
{ {
schedule.Play = false; kvp.Key.Play = false;
kvp.Value.Stop();
} }
_schedules.Clear(); _audioStreams.Clear();
} }
public override void StopScheduledSound(string filename) public override void StopScheduledSound(string filename)
{ {
foreach (var schedule in _schedules.ToArray()) foreach (var kvp in _audioStreams)
{ {
if (schedule.Filename != filename) continue; if (kvp.Key.Filename != filename) continue;
schedule.Play = false; kvp.Key.Play = false;
_schedules.Remove(schedule); kvp.Value.Stop();
_audioStreams.Remove(kvp.Key);
} }
} }
public override void AddScheduledSound(ScheduledSound schedule) public override void AddScheduledSound(ScheduledSound schedule)
{ {
_schedules.Add(schedule);
Play(schedule); Play(schedule);
} }
@@ -54,16 +55,11 @@ namespace Content.Client.GameObjects.Components.Sound
{ {
if (!schedule.Play) return; // We make sure this hasn't changed. if (!schedule.Play) return; // We make sure this hasn't changed.
if (_audioSystem == null) _audioSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>(); if (_audioSystem == null) _audioSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
_audioSystem.Play(schedule.Filename, Owner, schedule.AudioParams); _audioStreams.Add(schedule,_audioSystem.Play(schedule.Filename, Owner, schedule.AudioParams));
if (schedule.Times == 0) if (schedule.Times == 0) return;
{
_schedules.Remove(schedule);
return;
}
if (schedule.Times > 0) if (schedule.Times > 0) schedule.Times--;
schedule.Times--;
Play(schedule); Play(schedule);
}); });

View File

@@ -25,8 +25,6 @@ namespace Content.Client.GameObjects.EntitySystems
[UsedImplicitly] [UsedImplicitly]
public sealed class CombatModeSystem : SharedCombatModeSystem public sealed class CombatModeSystem : SharedCombatModeSystem
{ {
private const float AttackTimeThreshold = 0.15f;
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud; [Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly IPlayerManager _playerManager; [Dependency] private readonly IPlayerManager _playerManager;
@@ -37,9 +35,6 @@ namespace Content.Client.GameObjects.EntitySystems
private InputSystem _inputSystem; private InputSystem _inputSystem;
public bool UseOrAttackIsDown { get; private set; }
private float _timeHeld;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -48,10 +43,8 @@ namespace Content.Client.GameObjects.EntitySystems
_gameHud.OnTargetingZoneChanged = OnTargetingZoneChanged; _gameHud.OnTargetingZoneChanged = OnTargetingZoneChanged;
_inputSystem = EntitySystemManager.GetEntitySystem<InputSystem>(); _inputSystem = EntitySystemManager.GetEntitySystem<InputSystem>();
_inputSystem.BindMap.BindFunction(ContentKeyFunctions.UseOrAttack, new InputHandler(this));
_inputSystem.BindMap.BindFunction(ContentKeyFunctions.ToggleCombatMode, _inputSystem.BindMap.BindFunction(ContentKeyFunctions.ToggleCombatMode,
InputCmdHandler.FromDelegate(CombatModeToggled)); InputCmdHandler.FromDelegate(CombatModeToggled));
_overlayManager.AddOverlay(new CombatModeOverlay(this));
} }
private void CombatModeToggled(ICommonSession session) private void CombatModeToggled(ICommonSession session)
@@ -60,20 +53,10 @@ namespace Content.Client.GameObjects.EntitySystems
{ {
EntityManager.RaisePredictiveEvent( EntityManager.RaisePredictiveEvent(
new CombatModeSystemMessages.SetCombatModeActiveMessage(!IsInCombatMode())); new CombatModeSystemMessages.SetCombatModeActiveMessage(!IsInCombatMode()));
// Just in case.
UseOrAttackIsDown = false;
} }
} }
public override void Shutdown() public bool IsInCombatMode()
{
base.Shutdown();
_overlayManager.RemoveOverlay(nameof(CombatModeOverlay));
}
private bool IsInCombatMode()
{ {
var entity = _playerManager.LocalPlayer.ControlledEntity; var entity = _playerManager.LocalPlayer.ControlledEntity;
if (entity == null || !entity.TryGetComponent(out CombatModeComponent combatMode)) if (entity == null || !entity.TryGetComponent(out CombatModeComponent combatMode))
@@ -92,104 +75,6 @@ namespace Content.Client.GameObjects.EntitySystems
private void OnCombatModeChanged(bool obj) private void OnCombatModeChanged(bool obj)
{ {
EntityManager.RaisePredictiveEvent(new CombatModeSystemMessages.SetCombatModeActiveMessage(obj)); EntityManager.RaisePredictiveEvent(new CombatModeSystemMessages.SetCombatModeActiveMessage(obj));
// Just in case.
UseOrAttackIsDown = false;
}
private bool HandleInputMessage(ICommonSession session, InputCmdMessage message)
{
if (!(message is FullInputCmdMessage msg))
return false;
void SendMsg(BoundKeyFunction function, BoundKeyState state)
{
var functionId = _inputManager.NetworkBindMap.KeyFunctionID(function);
var sendMsg = new FullInputCmdMessage(msg.Tick, functionId, state,
msg.Coordinates, msg.ScreenCoordinates, msg.Uid);
_inputSystem.HandleInputCommand(session, function, sendMsg);
}
// If we are not in combat mode, relay it as a regular Use instead.
if (!IsInCombatMode())
{
SendMsg(EngineKeyFunctions.Use, msg.State);
return true;
}
if (msg.State == BoundKeyState.Down)
{
UseOrAttackIsDown = true;
_timeHeld = 0;
return true;
}
// Up.
if (UseOrAttackIsDown && _timeHeld >= AttackTimeThreshold)
{
// Attack.
SendMsg(ContentKeyFunctions.Attack, BoundKeyState.Down);
SendMsg(ContentKeyFunctions.Attack, BoundKeyState.Up);
}
else
{
// Use.
SendMsg(EngineKeyFunctions.Use, BoundKeyState.Down);
SendMsg(EngineKeyFunctions.Use, BoundKeyState.Up);
}
UseOrAttackIsDown = false;
return true;
}
public override void FrameUpdate(float frameTime)
{
if (UseOrAttackIsDown)
{
_timeHeld += frameTime;
}
}
// Custom input handler type so we get the ENTIRE InputCmdMessage.
private sealed class InputHandler : InputCmdHandler
{
private readonly CombatModeSystem _combatModeSystem;
public InputHandler(CombatModeSystem combatModeSystem)
{
_combatModeSystem = combatModeSystem;
}
public override bool HandleCmdMessage(ICommonSession session, InputCmdMessage message)
{
return _combatModeSystem.HandleInputMessage(session, message);
}
}
private sealed class CombatModeOverlay : Overlay
{
private readonly CombatModeSystem _system;
public CombatModeOverlay(CombatModeSystem system) : base(nameof(CombatModeOverlay))
{
_system = system;
}
protected override void Draw(DrawingHandleBase handle)
{
var screenHandle = (DrawingHandleScreen) handle;
var mousePos = IoCManager.Resolve<IInputManager>().MouseScreenPosition;
if (_system.UseOrAttackIsDown && _system._timeHeld > AttackTimeThreshold)
{
var tex = ResC.GetTexture($"/Textures/Objects/Tools/toolbox_r.png");
screenHandle.DrawTextureRect(tex, UIBox2.FromDimensions(mousePos, tex.Size * 2));
}
}
} }
} }
} }

View File

@@ -39,8 +39,8 @@ namespace Content.Client.GameObjects.EntitySystems
base.Update(frameTime); base.Update(frameTime);
var canFireSemi = _isFirstShot; var canFireSemi = _isFirstShot;
var state = _inputSystem.CmdStates.GetState(ContentKeyFunctions.Attack); var state = _inputSystem.CmdStates.GetState(EngineKeyFunctions.Use);
if (!_combatModeSystem.UseOrAttackIsDown && state != BoundKeyState.Down) if (!_combatModeSystem.IsInCombatMode() || state != BoundKeyState.Down)
{ {
_isFirstShot = true; _isFirstShot = true;
_blocked = false; _blocked = false;

View File

@@ -150,7 +150,10 @@ namespace Content.Client.GameObjects.EntitySystems
if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity)) if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity))
continue; continue;
var disabled = verb.GetVisibility(user, component) != VerbVisibility.Visible; if (VerbUtility.IsVerbInvisible(verb, user, component, out var vis))
continue;
var disabled = vis != VerbVisibility.Visible;
var category = verb.GetCategory(user, component); var category = verb.GetCategory(user, component);
@@ -166,7 +169,10 @@ namespace Content.Client.GameObjects.EntitySystems
if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity)) if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity))
continue; continue;
var disabled = globalVerb.GetVisibility(user, entity) != VerbVisibility.Visible; if (VerbUtility.IsVerbInvisible(globalVerb, user, entity, out var vis))
continue;
var disabled = vis != VerbVisibility.Visible;
var category = globalVerb.GetCategory(user, entity); var category = globalVerb.GetCategory(user, entity);
if(!buttons.ContainsKey(category)) if(!buttons.ContainsKey(category))

View File

@@ -15,7 +15,8 @@ namespace Content.Client.Input
common.AddFunction(ContentKeyFunctions.FocusChat); common.AddFunction(ContentKeyFunctions.FocusChat);
common.AddFunction(ContentKeyFunctions.ExamineEntity); common.AddFunction(ContentKeyFunctions.ExamineEntity);
common.AddFunction(ContentKeyFunctions.OpenTutorial); common.AddFunction(ContentKeyFunctions.OpenTutorial);
common.AddFunction(ContentKeyFunctions.UseOrAttack); common.AddFunction(ContentKeyFunctions.TakeScreenshot);
common.AddFunction(ContentKeyFunctions.TakeScreenshotNoUI);
var human = contexts.GetContext("human"); var human = contexts.GetContext("human");
human.AddFunction(ContentKeyFunctions.SwapHands); human.AddFunction(ContentKeyFunctions.SwapHands);
@@ -27,9 +28,11 @@ namespace Content.Client.Input
human.AddFunction(ContentKeyFunctions.OpenContextMenu); human.AddFunction(ContentKeyFunctions.OpenContextMenu);
human.AddFunction(ContentKeyFunctions.OpenCraftingMenu); human.AddFunction(ContentKeyFunctions.OpenCraftingMenu);
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu); human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
human.AddFunction(ContentKeyFunctions.SmartEquipBackpack);
human.AddFunction(ContentKeyFunctions.SmartEquipBelt);
human.AddFunction(ContentKeyFunctions.MouseMiddle); human.AddFunction(ContentKeyFunctions.MouseMiddle);
human.AddFunction(ContentKeyFunctions.ToggleCombatMode); human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
human.AddFunction(ContentKeyFunctions.Attack); human.AddFunction(ContentKeyFunctions.WideAttack);
var ghost = contexts.New("ghost", "common"); var ghost = contexts.New("ghost", "common");
ghost.AddFunction(EngineKeyFunctions.MoveUp); ghost.AddFunction(EngineKeyFunctions.MoveUp);

View File

@@ -0,0 +1,85 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Content.Shared.Input;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Input;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.Resources;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Utility;
using SixLabors.ImageSharp;
namespace Content.Client
{
internal class ScreenshotHook : IScreenshotHook
{
private static readonly ResourcePath BaseScreenshotPath = new ResourcePath("/Screenshots");
[Dependency] private readonly IInputManager _inputManager = default;
[Dependency] private readonly IClyde _clyde = default;
[Dependency] private readonly IResourceManager _resourceManager = default;
public void Initialize()
{
_inputManager.SetInputCommand(ContentKeyFunctions.TakeScreenshot, InputCmdHandler.FromDelegate(_ =>
{
Take(ScreenshotType.AfterUI);
}));
_inputManager.SetInputCommand(ContentKeyFunctions.TakeScreenshotNoUI, InputCmdHandler.FromDelegate(_ =>
{
Take(ScreenshotType.BeforeUI);
}));
}
private async void Take(ScreenshotType type)
{
var screenshot = await _clyde.ScreenshotAsync(type);
var time = DateTime.Now.ToString("yyyy-M-dd_HH.mm.ss");
if (!_resourceManager.UserData.IsDir(BaseScreenshotPath))
{
_resourceManager.UserData.CreateDir(BaseScreenshotPath);
}
for (var i = 0; i < 5; i++)
{
try
{
var filename = time;
if (i != 0)
{
filename = $"{filename}-{i}";
}
await using var file =
_resourceManager.UserData.Open(BaseScreenshotPath / $"{filename}.png", FileMode.CreateNew);
await Task.Run(() =>
{
// Saving takes forever, so don't hang the game on it.
screenshot.SaveAsPng(file);
});
Logger.InfoS("screenshot", "Screenshot taken as {0}.png", filename);
return;
}
catch (IOException e)
{
Logger.WarningS("screenshot", "Failed to save screenshot, retrying?:\n{0}", e);
}
}
Logger.ErrorS("screenshot", "Unable to save screenshot.");
}
}
public interface IScreenshotHook
{
void Initialize();
}
}

View File

@@ -7,6 +7,7 @@ using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -57,10 +58,10 @@ namespace Content.Client.UserInterface
AddChild(hBox); AddChild(hBox);
_leftButton.OnPressed += args => HandKeyBindDown(args.Event, HandNameLeft); _leftButton.OnPressed += args => HandKeyBindDown(args, HandNameLeft);
_leftButton.OnStoragePressed += args => _OnStoragePressed(args.Event, HandNameLeft); _leftButton.OnStoragePressed += args => _OnStoragePressed(args, HandNameLeft);
_rightButton.OnPressed += args => HandKeyBindDown(args.Event, HandNameRight); _rightButton.OnPressed += args => HandKeyBindDown(args, HandNameRight);
_rightButton.OnStoragePressed += args => _OnStoragePressed(args.Event, HandNameRight); _rightButton.OnStoragePressed += args => _OnStoragePressed(args, HandNameRight);
// Active hand // Active hand
_leftButton.AddChild(ActiveHandRect = new TextureRect _leftButton.AddChild(ActiveHandRect = new TextureRect
@@ -118,31 +119,34 @@ namespace Content.Client.UserInterface
private void HandKeyBindDown(GUIBoundKeyEventArgs args, string handIndex) private void HandKeyBindDown(GUIBoundKeyEventArgs args, string handIndex)
{ {
args.Handle();
if (!TryGetHands(out var hands)) if (!TryGetHands(out var hands))
return; return;
if (args.Function == ContentKeyFunctions.MouseMiddle) if (args.Function == ContentKeyFunctions.MouseMiddle)
{ {
hands.SendChangeHand(handIndex); hands.SendChangeHand(handIndex);
args.Handle();
return; return;
} }
var entity = hands.GetEntity(handIndex); var entity = hands.GetEntity(handIndex);
if (entity == null) if (entity == null)
{ {
if (args.CanFocus && hands.ActiveIndex != handIndex) if (args.Function == EngineKeyFunctions.UIClick && hands.ActiveIndex != handIndex)
{ {
hands.SendChangeHand(handIndex); hands.SendChangeHand(handIndex);
args.Handle();
} }
return; return;
} }
if (_itemSlotManager.OnButtonPressed(args, entity)) if (_itemSlotManager.OnButtonPressed(args, entity))
{
args.Handle();
return; return;
}
if (args.CanFocus) if (args.Function == EngineKeyFunctions.UIClick)
{ {
if (hands.ActiveIndex == handIndex) if (hands.ActiveIndex == handIndex)
{ {
@@ -152,13 +156,14 @@ namespace Content.Client.UserInterface
{ {
hands.AttackByInHand(handIndex); hands.AttackByInHand(handIndex);
} }
args.Handle();
return; return;
} }
} }
private void _OnStoragePressed(GUIBoundKeyEventArgs args, string handIndex) private void _OnStoragePressed(GUIBoundKeyEventArgs args, string handIndex)
{ {
if (!args.CanFocus) if (args.Function != EngineKeyFunctions.UIClick)
return; return;
if (!TryGetHands(out var hands)) if (!TryGetHands(out var hands))
return; return;

View File

@@ -1,5 +1,7 @@
using System; using System;
using Content.Shared.Input;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Maths; using Robust.Shared.Maths;
@@ -8,26 +10,26 @@ namespace Content.Client.GameObjects
{ {
public sealed class ItemSlotButton : MarginContainer public sealed class ItemSlotButton : MarginContainer
{ {
public BaseButton Button { get; } public TextureRect Button { get; }
public SpriteView SpriteView { get; } public SpriteView SpriteView { get; }
public BaseButton StorageButton { get; } public BaseButton StorageButton { get; }
public TextureRect CooldownCircle { get; } public TextureRect CooldownCircle { get; }
public Action<BaseButton.ButtonEventArgs> OnPressed { get; set; } public Action<GUIBoundKeyEventArgs> OnPressed { get; set; }
public Action<BaseButton.ButtonEventArgs> OnStoragePressed { get; set; } public Action<GUIBoundKeyEventArgs> OnStoragePressed { get; set; }
public ItemSlotButton(Texture texture, Texture storageTexture) public ItemSlotButton(Texture texture, Texture storageTexture)
{ {
CustomMinimumSize = (64, 64); CustomMinimumSize = (64, 64);
AddChild(Button = new TextureButton AddChild(Button = new TextureRect
{ {
TextureNormal = texture, Texture = texture,
Scale = (2, 2), TextureScale = (2, 2),
EnableAllKeybinds = true MouseFilter = MouseFilterMode.Stop
}); });
Button.OnPressed += OnButtonPressed; Button.OnKeyBindDown += OnButtonPressed;
AddChild(SpriteView = new SpriteView AddChild(SpriteView = new SpriteView
{ {
@@ -42,9 +44,16 @@ namespace Content.Client.GameObjects
SizeFlagsHorizontal = SizeFlags.ShrinkEnd, SizeFlagsHorizontal = SizeFlags.ShrinkEnd,
SizeFlagsVertical = SizeFlags.ShrinkEnd, SizeFlagsVertical = SizeFlags.ShrinkEnd,
Visible = false, Visible = false,
EnableAllKeybinds = true
}); });
StorageButton.OnKeyBindDown += args =>
{
if (args.Function != EngineKeyFunctions.UIClick)
{
OnButtonPressed(args);
}
};
StorageButton.OnPressed += OnStorageButtonPressed; StorageButton.OnPressed += OnStorageButtonPressed;
AddChild(CooldownCircle = new TextureRect AddChild(CooldownCircle = new TextureRect
@@ -57,20 +66,20 @@ namespace Content.Client.GameObjects
}); });
} }
private void OnButtonPressed(BaseButton.ButtonEventArgs args) private void OnButtonPressed(GUIBoundKeyEventArgs args)
{ {
OnPressed?.Invoke(args); OnPressed?.Invoke(args);
} }
private void OnStorageButtonPressed(BaseButton.ButtonEventArgs args) private void OnStorageButtonPressed(BaseButton.ButtonEventArgs args)
{ {
if (args.Event.Function == EngineKeyFunctions.Use) if (args.Event.Function == EngineKeyFunctions.UIClick)
{ {
OnStoragePressed?.Invoke(args); OnStoragePressed?.Invoke(args.Event);
} }
else else
{ {
OnPressed?.Invoke(args); OnPressed?.Invoke(args.Event);
} }
} }
} }

View File

@@ -65,8 +65,6 @@ namespace Content.Client.UserInterface
public bool OnButtonPressed(GUIBoundKeyEventArgs args, IEntity item) public bool OnButtonPressed(GUIBoundKeyEventArgs args, IEntity item)
{ {
args.Handle();
if (item == null) if (item == null)
return false; return false;
@@ -99,6 +97,7 @@ namespace Content.Client.UserInterface
{ {
return false; return false;
} }
args.Handle();
return true; return true;
} }

View File

@@ -21,6 +21,7 @@ namespace Content.Client.UserInterface.Stylesheets
public const string StyleClassLabelSecondaryColor = "LabelSecondaryColor"; public const string StyleClassLabelSecondaryColor = "LabelSecondaryColor";
public const string StyleClassLabelBig = "LabelBig"; public const string StyleClassLabelBig = "LabelBig";
public const string StyleClassButtonBig = "ButtonBig"; public const string StyleClassButtonBig = "ButtonBig";
public const string StyleClassPopupMessage = "PopupMessage";
public static readonly Color NanoGold = Color.FromHex("#A88B5E"); public static readonly Color NanoGold = Color.FromHex("#A88B5E");
@@ -41,7 +42,9 @@ namespace Content.Client.UserInterface.Stylesheets
public StyleNano(IResourceCache resCache) : base(resCache) public StyleNano(IResourceCache resCache) : base(resCache)
{ {
var notoSans10 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 10); var notoSans10 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 10);
var notoSansItalic10 = resCache.GetFont("/Nano/NotoSans/NotoSans-Italic.ttf", 10);
var notoSans12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 12); var notoSans12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 12);
var notoSansItalic12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Italic.ttf", 12);
var notoSansBold12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Bold.ttf", 12); var notoSansBold12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Bold.ttf", 12);
var notoSansDisplayBold14 = resCache.GetFont("/Fonts/NotoSansDisplay/NotoSansDisplay-Bold.ttf", 14); var notoSansDisplayBold14 = resCache.GetFont("/Fonts/NotoSansDisplay/NotoSansDisplay-Bold.ttf", 14);
var notoSans16 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 16); var notoSans16 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 16);
@@ -449,6 +452,19 @@ namespace Content.Client.UserInterface.Stylesheets
new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox) new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox)
}), }),
new StyleRule(new SelectorElement(typeof(PanelContainer), new[] {"speechBox", "sayBox"}, null, null), new[]
{
new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox)
}),
new StyleRule(new SelectorChild(
new SelectorElement(typeof(PanelContainer), new[] {"speechBox", "emoteBox"}, null, null),
new SelectorElement(typeof(RichTextLabel), null, null, null)),
new[]
{
new StyleProperty("font", notoSansItalic12),
}),
// Entity tooltip // Entity tooltip
new StyleRule( new StyleRule(
new SelectorElement(typeof(PanelContainer), new[] {ExamineSystem.StyleClassEntityTooltip}, null, new SelectorElement(typeof(PanelContainer), new[] {ExamineSystem.StyleClassEntityTooltip}, null,
@@ -552,6 +568,14 @@ namespace Content.Client.UserInterface.Stylesheets
new StyleProperty("font", notoSans16) new StyleProperty("font", notoSans16)
}), }),
// Popup messages
new StyleRule(new SelectorElement(typeof(Label), new[] {StyleClassPopupMessage}, null, null),
new[]
{
new StyleProperty("font", notoSansItalic10),
new StyleProperty("font-color", Color.LightGray),
}),
//APC and SMES power state label colors //APC and SMES power state label colors
new StyleRule(new SelectorElement(typeof(Label), new[] {StyleClassPowerStateNone}, null, null), new[] new StyleRule(new SelectorElement(typeof(Label), new[] {StyleClassPowerStateNone}, null, null), new[]
{ {

View File

@@ -69,10 +69,14 @@ namespace Content.Client.UserInterface
Switch hands: [color=#a4885c]{4}[/color] Switch hands: [color=#a4885c]{4}[/color]
Use held item: [color=#a4885c]{5}[/color] Use held item: [color=#a4885c]{5}[/color]
Drop held item: [color=#a4885c]{6}[/color] Drop held item: [color=#a4885c]{6}[/color]
Smart equip from backpack: [color=#a4885c]{24}[/color]
Smart equip from belt: [color=#a4885c]{25}[/color]
Open inventory: [color=#a4885c]{7}[/color] Open inventory: [color=#a4885c]{7}[/color]
Open character window: [color=#a4885c]{8}[/color] Open character window: [color=#a4885c]{8}[/color]
Open crafting window: [color=#a4885c]{9}[/color] Open crafting window: [color=#a4885c]{9}[/color]
Focus chat: [color=#a4885c]{10}[/color] Focus chat: [color=#a4885c]{10}[/color]
Use hand/object in hand: [color=#a4885c]{22}[/color]
Do wide attack: [color=#a4885c]{23}[/color]
Use targeted entity: [color=#a4885c]{11}[/color] Use targeted entity: [color=#a4885c]{11}[/color]
Throw held item: [color=#a4885c]{12}[/color] Throw held item: [color=#a4885c]{12}[/color]
Examine entity: [color=#a4885c]{13}[/color] Examine entity: [color=#a4885c]{13}[/color]
@@ -102,16 +106,20 @@ Toggle sandbox window: [color=#a4885c]{21}[/color]",
Key(ShowDebugMonitors), Key(ShowDebugMonitors),
Key(OpenEntitySpawnWindow), Key(OpenEntitySpawnWindow),
Key(OpenTileSpawnWindow), Key(OpenTileSpawnWindow),
Key(OpenSandboxWindow))); Key(OpenSandboxWindow),
Key(Use),
//Gameplay Key(WideAttack),
VBox.AddChild(new Label { FontOverride = headerFont, Text = Loc.GetString("\nSandbox spawner", Key(OpenSandboxWindow)) }); Key(SmartEquipBackpack),
AddFormattedText(SandboxSpawnerContents); Key(SmartEquipBelt)));
//Gameplay //Gameplay
VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nGameplay" }); VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nGameplay" });
AddFormattedText(GameplayContents); AddFormattedText(GameplayContents);
//Gameplay
VBox.AddChild(new Label { FontOverride = headerFont, Text = Loc.GetString("\nSandbox spawner", Key(OpenSandboxWindow)) });
AddFormattedText(SandboxSpawnerContents);
//Feedback //Feedback
VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nFeedback" }); VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nFeedback" });
AddFormattedText(FeedbackContents); AddFormattedText(FeedbackContents);

View File

@@ -0,0 +1,64 @@
using System.Threading.Tasks;
using Content.Client.GameObjects.Components.Gravity;
using Content.Server.GameObjects.Components.Gravity;
using Content.Server.GameObjects.Components.Power;
using NUnit.Framework;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.IntegrationTests.Tests
{
/// Tests the behavior of GravityGeneratorComponent,
/// making sure that gravity is applied to the correct grids.
[TestFixture]
[TestOf(typeof(GravityGeneratorComponent))]
public class GravityGridTest : ContentIntegrationTest
{
[Test]
public async Task Test()
{
var server = StartServerDummyTicker();
IEntity generator = null;
IMapGrid grid1 = null;
IMapGrid grid2 = null;
// Create grids
server.Assert(() =>
{
var mapMan = IoCManager.Resolve<IMapManager>();
mapMan.CreateMap(new MapId(1));
grid1 = mapMan.CreateGrid(new MapId(1));
grid2 = mapMan.CreateGrid(new MapId(1));
var entityMan = IoCManager.Resolve<IEntityManager>();
generator = entityMan.SpawnEntity("GravityGenerator", new GridCoordinates(new Vector2(0, 0), grid2.Index));
Assert.That(generator.HasComponent<GravityGeneratorComponent>());
Assert.That(generator.HasComponent<PowerDeviceComponent>());
var generatorComponent = generator.GetComponent<GravityGeneratorComponent>();
var powerComponent = generator.GetComponent<PowerDeviceComponent>();
Assert.AreEqual(generatorComponent.Status, GravityGeneratorStatus.Unpowered);
powerComponent.ExternalPowered = true;
});
server.RunTicks(1);
server.Assert(() =>
{
var generatorComponent = generator.GetComponent<GravityGeneratorComponent>();
Assert.AreEqual(generatorComponent.Status, GravityGeneratorStatus.On);
Assert.That(!grid1.HasGravity);
Assert.That(grid2.HasGravity);
});
await server.WaitIdleAsync();
}
}
}

View File

@@ -4,6 +4,7 @@ using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameTicking; using Content.Server.Interfaces.GameTicking;
using Content.Server.Preferences; using Content.Server.Preferences;
using Content.Server.Sandbox; using Content.Server.Sandbox;
using Content.Shared.Kitchen;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
@@ -70,6 +71,7 @@ namespace Content.Server
logManager.GetSawmill("Storage").Level = LogLevel.Info; logManager.GetSawmill("Storage").Level = LogLevel.Info;
IoCManager.Resolve<IServerPreferencesManager>().StartInit(); IoCManager.Resolve<IServerPreferencesManager>().StartInit();
} }
public override void PostInit() public override void PostInit()
@@ -79,6 +81,7 @@ namespace Content.Server
_gameTicker.Initialize(); _gameTicker.Initialize();
IoCManager.Resolve<ISandboxManager>().Initialize(); IoCManager.Resolve<ISandboxManager>().Initialize();
IoCManager.Resolve<IServerPreferencesManager>().FinishInit(); IoCManager.Resolve<IServerPreferencesManager>().FinishInit();
IoCManager.Resolve<RecipeManager>().Initialize();
} }
public override void Update(ModUpdateLevel level, FrameEventArgs frameEventArgs) public override void Update(ModUpdateLevel level, FrameEventArgs frameEventArgs)

View File

@@ -151,6 +151,12 @@ namespace Content.Server.GameObjects
return success; return success;
} }
public void PutInHandOrDrop(ItemComponent item)
{
if (!PutInHand(item))
item.Owner.Transform.GridPosition = Owner.Transform.GridPosition;
}
public bool CanPutInHand(ItemComponent item) public bool CanPutInHand(ItemComponent item)
{ {
foreach (var hand in ActivePriorityEnumerable()) foreach (var hand in ActivePriorityEnumerable())

View File

@@ -0,0 +1,206 @@
using Content.Server.GameObjects.Components.Damage;
using Content.Server.GameObjects.Components.Interactable.Tools;
using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Shared.GameObjects.Components.Gravity;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Gravity
{
[RegisterComponent]
public class GravityGeneratorComponent: SharedGravityGeneratorComponent, IAttackBy, IBreakAct, IAttackHand
{
private BoundUserInterface _userInterface;
private PowerDeviceComponent _powerDevice;
private SpriteComponent _sprite;
private bool _switchedOn;
private bool _intact;
private GravityGeneratorStatus _status;
public bool Powered => _powerDevice.Powered;
public bool SwitchedOn => _switchedOn;
public bool Intact => _intact;
public GravityGeneratorStatus Status => _status;
public bool NeedsUpdate
{
get
{
switch (_status)
{
case GravityGeneratorStatus.On:
return !(Powered && SwitchedOn && Intact);
case GravityGeneratorStatus.Off:
return SwitchedOn || !(Powered && Intact);
case GravityGeneratorStatus.Unpowered:
return SwitchedOn || Powered || !Intact;
case GravityGeneratorStatus.Broken:
return SwitchedOn || Powered || Intact;
default:
return true; // This _should_ be unreachable
}
}
}
public override string Name => "GravityGenerator";
public override void Initialize()
{
base.Initialize();
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
.GetBoundUserInterface(GravityGeneratorUiKey.Key);
_userInterface.OnReceiveMessage += HandleUIMessage;
_powerDevice = Owner.GetComponent<PowerDeviceComponent>();
_sprite = Owner.GetComponent<SpriteComponent>();
_switchedOn = true;
_intact = true;
_status = GravityGeneratorStatus.On;
UpdateState();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _switchedOn, "switched_on", true);
serializer.DataField(ref _intact, "intact", true);
}
bool IAttackHand.AttackHand(AttackHandEventArgs eventArgs)
{
if (!eventArgs.User.TryGetComponent<IActorComponent>(out var actor))
return false;
if (Status != GravityGeneratorStatus.Off && Status != GravityGeneratorStatus.On)
{
return false;
}
OpenUserInterface(actor.playerSession);
return true;
}
public bool AttackBy(AttackByEventArgs eventArgs)
{
if (!eventArgs.AttackWith.TryGetComponent<WelderComponent>(out var welder)) return false;
if (welder.TryUse(5.0f))
{
// Repair generator
var damagable = Owner.GetComponent<DamageableComponent>();
var breakable = Owner.GetComponent<BreakableComponent>();
damagable.HealAllDamage();
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;
} else
{
return false;
}
}
public void OnBreak(BreakageEventArgs eventArgs)
{
_intact = false;
_switchedOn = false;
}
public void UpdateState()
{
if (!Intact)
{
MakeBroken();
} else if (!Powered)
{
MakeUnpowered();
} else if (!SwitchedOn)
{
MakeOff();
} else
{
MakeOn();
}
}
private void HandleUIMessage(ServerBoundUserInterfaceMessage message)
{
switch (message.Message)
{
case GeneratorStatusRequestMessage _:
_userInterface.SetState(new GeneratorState(Status == GravityGeneratorStatus.On));
break;
case SwitchGeneratorMessage msg:
_switchedOn = msg.On;
UpdateState();
break;
default:
break;
}
}
private void OpenUserInterface(IPlayerSession playerSession)
{
_userInterface.Open(playerSession);
}
private void MakeBroken()
{
_status = GravityGeneratorStatus.Broken;
_sprite.LayerSetState(0, "broken");
_sprite.LayerSetVisible(1, false);
}
private void MakeUnpowered()
{
_status = GravityGeneratorStatus.Unpowered;
_sprite.LayerSetState(0, "off");
_sprite.LayerSetVisible(1, false);
}
private void MakeOff()
{
_status = GravityGeneratorStatus.Off;
_sprite.LayerSetState(0, "off");
_sprite.LayerSetVisible(1, false);
}
private void MakeOn()
{
_status = GravityGeneratorStatus.On;
_sprite.LayerSetState(0, "on");
_sprite.LayerSetVisible(1, true);
}
}
public enum GravityGeneratorStatus
{
Broken,
Unpowered,
Off,
On
}
}

View File

@@ -45,6 +45,8 @@ namespace Content.Server.GameObjects
private int StorageCapacityMax = 10000; private int StorageCapacityMax = 10000;
public HashSet<IPlayerSession> SubscribedSessions = new HashSet<IPlayerSession>(); public HashSet<IPlayerSession> SubscribedSessions = new HashSet<IPlayerSession>();
public IReadOnlyCollection<IEntity> StoredEntities => storage.ContainedEntities;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -140,7 +142,6 @@ namespace Content.Server.GameObjects
/// <returns></returns> /// <returns></returns>
public bool AttackBy(AttackByEventArgs eventArgs) public bool AttackBy(AttackByEventArgs eventArgs)
{ {
_ensureInitialCalculated();
Logger.DebugS("Storage", "Storage (UID {0}) attacked by user (UID {1}) with entity (UID {2}).", Owner.Uid, eventArgs.User.Uid, eventArgs.AttackWith.Uid); Logger.DebugS("Storage", "Storage (UID {0}) attacked by user (UID {1}) with entity (UID {2}).", Owner.Uid, eventArgs.User.Uid, eventArgs.AttackWith.Uid);
if(Owner.TryGetComponent<PlaceableSurfaceComponent>(out var placeableSurfaceComponent)) if(Owner.TryGetComponent<PlaceableSurfaceComponent>(out var placeableSurfaceComponent))
@@ -363,8 +364,10 @@ namespace Content.Server.GameObjects
/// <summary> /// <summary>
/// Inserts an entity into the storage component from the players active hand. /// Inserts an entity into the storage component from the players active hand.
/// </summary> /// </summary>
private bool PlayerInsertEntity(IEntity player) public bool PlayerInsertEntity(IEntity player)
{ {
_ensureInitialCalculated();
if (!player.TryGetComponent(out IHandsComponent hands) || hands.GetActiveHand == null) if (!player.TryGetComponent(out IHandsComponent hands) || hands.GetActiveHand == null)
return false; return false;

View File

@@ -0,0 +1,408 @@
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
using Content.Server.GameObjects.Components.Chemistry;
using Content.Server.GameObjects.Components.Nutrition;
using Content.Shared.Chemistry;
using Robust.Shared.Serialization;
using Robust.Shared.Interfaces.GameObjects;
using Content.Shared.Prototypes.Kitchen;
using Content.Shared.Kitchen;
using Robust.Shared.Timers;
using Robust.Server.GameObjects;
using Content.Shared.GameObjects.Components.Power;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.GameObjects.Components.Container;
using Content.Server.GameObjects.Components.Power;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Localization;
using Content.Server.Interfaces;
using Robust.Shared.Audio;
using YamlDotNet.Serialization.NodeTypeResolvers;
namespace Content.Server.GameObjects.Components.Kitchen
{
[RegisterComponent]
[ComponentReference(typeof(IActivate))]
public class KitchenMicrowaveComponent : SharedMicrowaveComponent, IActivate, IAttackBy, ISolutionChange
{
#pragma warning disable 649
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
[Dependency] private readonly IEntityManager _entityManager;
[Dependency] private readonly RecipeManager _recipeManager;
[Dependency] private readonly IServerNotifyManager _notifyManager;
#pragma warning restore 649
#region YAMLSERIALIZE
private int _cookTimeDefault;
private int _cookTimeMultiplier; //For upgrades and stuff I guess?
private string _badRecipeName;
private string _startCookingSound;
private string _cookingCompleteSound;
#endregion
#region VIEWVARIABLES
[ViewVariables]
private SolutionComponent _solution;
[ViewVariables]
private bool _busy = false;
/// <summary>
/// This is a fixed offset of 5.
/// The cook times for all recipes should be divisible by 5,with a minimum of 1 second.
/// For right now, I don't think any recipe cook time should be greater than 60 seconds.
/// </summary>
[ViewVariables]
private uint _currentCookTimerTime { get; set; } = 1;
#endregion
private bool Powered => _powerDevice.Powered;
private bool HasContents => _solution.ReagentList.Count > 0 || _storage.ContainedEntities.Count > 0;
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => UpdateUserInterface();
private AudioSystem _audioSystem;
private AppearanceComponent _appearance;
private PowerDeviceComponent _powerDevice;
private BoundUserInterface _userInterface;
private Container _storage;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _badRecipeName, "failureResult", "FoodBadRecipe");
serializer.DataField(ref _cookTimeDefault, "cookTime", 5);
serializer.DataField(ref _cookTimeMultiplier, "cookTimeMultiplier", 1000);
serializer.DataField(ref _startCookingSound, "beginCookingSound","/Audio/machines/microwave_start_beep.ogg" );
serializer.DataField(ref _cookingCompleteSound, "foodDoneSound","/Audio/machines/microwave_done_beep.ogg" );
}
public override void Initialize()
{
base.Initialize();
_solution ??= Owner.TryGetComponent(out SolutionComponent solutionComponent)
? solutionComponent
: Owner.AddComponent<SolutionComponent>();
_storage = ContainerManagerComponent.Ensure<Container>("microwave_entity_container", Owner, out var existed);
_appearance = Owner.GetComponent<AppearanceComponent>();
_powerDevice = Owner.GetComponent<PowerDeviceComponent>();
_audioSystem = _entitySystemManager.GetEntitySystem<AudioSystem>();
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
.GetBoundUserInterface(MicrowaveUiKey.Key);
_userInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
}
private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage message)
{
if (!Powered || _busy)
{
return;
}
switch (message.Message)
{
case MicrowaveStartCookMessage msg :
wzhzhzh();
break;
case MicrowaveEjectMessage msg :
if (HasContents)
{
VaporizeReagents();
EjectSolids();
ClickSound();
UpdateUserInterface();
}
break;
case MicrowaveEjectSolidIndexedMessage msg:
if (HasContents)
{
EjectSolidWithIndex(msg.EntityID);
ClickSound();
UpdateUserInterface();
}
break;
case MicrowaveVaporizeReagentIndexedMessage msg:
if (HasContents)
{
VaporizeReagentWithReagentQuantity(msg.ReagentQuantity);
ClickSound();
UpdateUserInterface();
}
break;
case MicrowaveSelectCookTimeMessage msg:
_currentCookTimerTime = msg.newCookTime;
ClickSound();
UpdateUserInterface();
break;
}
}
private void SetAppearance(MicrowaveVisualState state)
{
if (_appearance != null || Owner.TryGetComponent(out _appearance))
{
_appearance.SetData(PowerDeviceVisuals.VisualState, state);
}
}
private void UpdateUserInterface()
{
var solidsVisualList = new List<EntityUid>();
foreach(var item in _storage.ContainedEntities)
{
solidsVisualList.Add(item.Uid);
}
_userInterface.SetState(new MicrowaveUpdateUserInterfaceState(_solution.Solution.Contents, solidsVisualList));
}
void IActivate.Activate(ActivateEventArgs eventArgs)
{
if (!eventArgs.User.TryGetComponent(out IActorComponent actor) || !Powered)
{
return;
}
UpdateUserInterface();
_userInterface.Open(actor.playerSession);
}
public bool AttackBy(AttackByEventArgs eventArgs)
{
var itemEntity = eventArgs.User.GetComponent<HandsComponent>().GetActiveHand.Owner;
if(itemEntity.TryGetComponent<PourableComponent>(out var attackPourable))
{
//Get target and check if it can be poured into
if (!Owner.TryGetComponent<SolutionComponent>(out var mySolution)
|| !mySolution.CanPourIn)
{
return false;
}
if (!itemEntity.TryGetComponent<SolutionComponent>(out var attackSolution)
|| !attackSolution.CanPourOut)
{
return false;
}
//Get transfer amount. May be smaller than _transferAmount if not enough room
var realTransferAmount = ReagentUnit.Min(attackPourable.TransferAmount, mySolution.EmptyVolume);
if (realTransferAmount <= 0) //Special message if container is full
{
_notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User,
Loc.GetString("Container is full"));
return false;
}
//Move units from attackSolution to targetSolution
var removedSolution = attackSolution.SplitSolution(realTransferAmount);
if (!mySolution.TryAddSolution(removedSolution))
{
return false;
}
_notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User,
Loc.GetString("Transferred {0}u", removedSolution.TotalVolume));
return true;
}
if (!itemEntity.TryGetComponent(typeof(FoodComponent), out var food))
{
return false;
}
var ent = food.Owner; //Get the entity of the ItemComponent.
_storage.Insert(ent);
UpdateUserInterface();
return true;
}
//This is required. It's 'cook'.
private void wzhzhzh()
{
if (!HasContents)
{
return;
}
_busy = true;
// Convert storage into Dictionary of ingredients
var solidsDict = new Dictionary<string, int>();
foreach(var item in _storage.ContainedEntities)
{
if(solidsDict.ContainsKey(item.Prototype.ID))
{
solidsDict[item.Prototype.ID]++;
}
else
{
solidsDict.Add(item.Prototype.ID, 1);
}
}
// Check recipes
FoodRecipePrototype recipeToCook = null;
foreach(var r in _recipeManager.Recipes)
{
if (!CanSatisfyRecipe(r, solidsDict))
{
continue;
}
recipeToCook = r;
}
var goodMeal = (recipeToCook != null)
&&
(_currentCookTimerTime == (uint)recipeToCook.CookTime) ? true : false;
SetAppearance(MicrowaveVisualState.Cooking);
_audioSystem.Play(_startCookingSound);
Timer.Spawn((int)(_currentCookTimerTime * _cookTimeMultiplier), () =>
{
if (goodMeal)
{
SubtractContents(recipeToCook);
}
else
{
VaporizeReagents();
VaporizeSolids();
}
var entityToSpawn = goodMeal ? recipeToCook.Result : _badRecipeName;
_entityManager.SpawnEntity(entityToSpawn, Owner.Transform.GridPosition);
_audioSystem.Play(_cookingCompleteSound);
SetAppearance(MicrowaveVisualState.Idle);
_busy = false;
});
UpdateUserInterface();
return;
}
private void VaporizeReagents()
{
_solution.RemoveAllSolution();
}
private void VaporizeReagentWithReagentQuantity(Solution.ReagentQuantity reagentQuantity)
{
_solution.TryRemoveReagent(reagentQuantity.ReagentId, reagentQuantity.Quantity);
}
private void VaporizeSolids()
{
for(var i = _storage.ContainedEntities.Count-1; i>=0; i--)
{
var item = _storage.ContainedEntities.ElementAt(i);
_storage.Remove(item);
item.Delete();
}
}
private void EjectSolids()
{
for(var i = _storage.ContainedEntities.Count-1; i>=0; i--)
{
_storage.Remove(_storage.ContainedEntities.ElementAt(i));
}
}
private void EjectSolidWithIndex(EntityUid entityID)
{
if (_entityManager.EntityExists(entityID))
{
_storage.Remove(_entityManager.GetEntity(entityID));
}
}
private void SubtractContents(FoodRecipePrototype recipe)
{
foreach(var recipeReagent in recipe.IngredientsReagents)
{
_solution.TryRemoveReagent(recipeReagent.Key, ReagentUnit.New(recipeReagent.Value));
}
foreach (var recipeSolid in recipe.IngredientsSolids)
{
for (var i = 0; i < recipeSolid.Value; i++)
{
foreach (var item in _storage.ContainedEntities)
{
if (item.Prototype.ID == recipeSolid.Key)
{
_storage.Remove(item);
item.Delete();
break;
}
}
}
}
}
private bool CanSatisfyRecipe(FoodRecipePrototype recipe, Dictionary<string,int> solids)
{
foreach (var reagent in recipe.IngredientsReagents)
{
if (!_solution.ContainsReagent(reagent.Key, out var amount))
{
return false;
}
if (amount.Int() < reagent.Value)
{
return false;
}
}
foreach (var solid in recipe.IngredientsSolids)
{
if (!solids.ContainsKey(solid.Key))
{
return false;
}
if (solids[solid.Key] < solid.Value)
{
return false;
}
}
return true;
}
private void ClickSound()
{
_audioSystem.Play("/Audio/machines/machine_switch.ogg", AudioParams.Default.WithVolume(-2f));
}
}
}

View File

@@ -118,9 +118,17 @@ namespace Content.Server.GameObjects.Components.Mobs
if (!ShowExamineInfo) if (!ShowExamineInfo)
return; return;
var dead = false;
if(Owner.TryGetComponent<SpeciesComponent>(out var species))
if (species.CurrentDamageState is DeadState)
dead = true;
// TODO: Use gendered pronouns depending on the entity // TODO: Use gendered pronouns depending on the entity
if(!HasMind) if(!HasMind)
message.AddMarkup($"[color=red]They are totally catatonic. The stresses of life in deep-space must have been too much for them. Any recovery is unlikely.[/color]"); message.AddMarkup(!dead
? $"[color=red]They are totally catatonic. The stresses of life in deep-space must have been too much for them. Any recovery is unlikely.[/color]"
: $"[color=purple]Their soul has departed.[/color]");
else if(Mind.Session == null) else if(Mind.Session == null)
message.AddMarkup("[color=yellow]They have a blank, absent-minded stare and appears completely unresponsive to anything. They may snap out of it soon.[/color]"); message.AddMarkup("[color=yellow]They have a blank, absent-minded stare and appears completely unresponsive to anything. They may snap out of it soon.[/color]");
} }

View File

@@ -177,7 +177,7 @@ namespace Content.Server.GameObjects
currentstate = threshold; currentstate = threshold;
EntityEventArgs toRaise = new MobDamageStateChangedMessage(this); var toRaise = new MobDamageStateChangedMessage(this);
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, toRaise); Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, toRaise);
} }

View File

@@ -90,6 +90,15 @@ namespace Content.Server.GameObjects.Components.Movement
} }
} }
/// <inheritdoc />
[ViewVariables]
public float CurrentPushSpeed => 5.0f;
/// <inheritdoc />
[ViewVariables]
public float GrabRange => 0.2f;
/// <summary> /// <summary>
/// Is the entity Sprinting (running)? /// Is the entity Sprinting (running)?
/// </summary> /// </summary>

View File

@@ -67,6 +67,14 @@ namespace Content.Server.GameObjects.Components.Movement
} }
} }
/// <inheritdoc />
[ViewVariables]
public float CurrentPushSpeed => 5.0f;
/// <inheritdoc />
[ViewVariables]
public float GrabRange => 0.2f;
/// <summary> /// <summary>
/// Is the entity Sprinting (running)? /// Is the entity Sprinting (running)?
/// </summary> /// </summary>

View File

@@ -34,6 +34,15 @@ namespace Content.Server.GameObjects.Components.Movement
[ViewVariables(VVAccess.ReadWrite)] [ViewVariables(VVAccess.ReadWrite)]
public float CurrentWalkSpeed { get; set; } = 8; public float CurrentWalkSpeed { get; set; } = 8;
public float CurrentSprintSpeed { get; set; } public float CurrentSprintSpeed { get; set; }
/// <inheritdoc />
[ViewVariables]
public float CurrentPushSpeed => 0.0f;
/// <inheritdoc />
[ViewVariables]
public float GrabRange => 0.0f;
public bool Sprinting { get; set; } public bool Sprinting { get; set; }
public Vector2 VelocityDir { get; } = Vector2.Zero; public Vector2 VelocityDir { get; } = Vector2.Zero;
public GridCoordinates LastPosition { get; set; } public GridCoordinates LastPosition { get; set; }

View File

@@ -1,4 +1,5 @@
using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Audio;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -16,17 +17,24 @@ namespace Content.Server.GameObjects.Components.Sound
public override string Name => "EmitSoundOnUse"; public override string Name => "EmitSoundOnUse";
public string _soundName; public string _soundName;
public float _pitchVariation;
public override void ExposeData(ObjectSerializer serializer) public override void ExposeData(ObjectSerializer serializer)
{ {
base.ExposeData(serializer); base.ExposeData(serializer);
serializer.DataField(ref _soundName, "sound", ""); serializer.DataField(ref _soundName, "sound", string.Empty);
serializer.DataField(ref _pitchVariation, "variation", 0.0f);
} }
bool IUse.UseEntity(UseEntityEventArgs eventArgs) bool IUse.UseEntity(UseEntityEventArgs eventArgs)
{ {
if (!string.IsNullOrWhiteSpace(_soundName)) if (!string.IsNullOrWhiteSpace(_soundName))
{ {
if (_pitchVariation > 0.0)
{
Owner.GetComponent<SoundComponent>().Play(_soundName, AudioHelpers.WithVariation(_pitchVariation).WithVolume(-2f));
return true;
}
Owner.GetComponent<SoundComponent>().Play(_soundName, AudioParams.Default.WithVolume(-2f)); Owner.GetComponent<SoundComponent>().Play(_soundName, AudioParams.Default.WithVolume(-2f));
return true; return true;
} }

View File

@@ -235,6 +235,14 @@ namespace Content.Server.GameObjects.Components
_notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You have no hands.")); _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You have no hands."));
return; return;
} }
var interactionSystem = IoCManager.Resolve<EntitySystemManager>().GetEntitySystem<InteractionSystem>();
if (!interactionSystem.InRangeUnobstructed(player.Transform.MapPosition, Owner.Transform.WorldPosition, ignoredEnt: Owner))
{
_notifyManager.PopupMessage(Owner.Transform.GridPosition, player, _localizationManager.GetString("You can't reach there!"));
return;
}
var activeHandEntity = handsComponent.GetActiveHand?.Owner; var activeHandEntity = handsComponent.GetActiveHand?.Owner;
activeHandEntity.TryGetComponent<ToolComponent>(out var tool); activeHandEntity.TryGetComponent<ToolComponent>(out var tool);
switch (msg.Action) switch (msg.Action)

View File

@@ -417,8 +417,8 @@ namespace Content.Server.GameObjects.EntitySystems
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>(); var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
inputSys.BindMap.BindFunction(EngineKeyFunctions.Use, inputSys.BindMap.BindFunction(EngineKeyFunctions.Use,
new PointerInputCmdHandler(HandleUseItemInHand)); new PointerInputCmdHandler(HandleUseItemInHand));
inputSys.BindMap.BindFunction(ContentKeyFunctions.Attack, inputSys.BindMap.BindFunction(ContentKeyFunctions.WideAttack,
new PointerInputCmdHandler(HandleAttack)); new PointerInputCmdHandler(HandleWideAttack));
inputSys.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInWorld, inputSys.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInWorld,
new PointerInputCmdHandler(HandleActivateItemInWorld)); new PointerInputCmdHandler(HandleActivateItemInWorld));
} }
@@ -477,7 +477,7 @@ namespace Content.Server.GameObjects.EntitySystems
activateComp.Activate(new ActivateEventArgs {User = user}); activateComp.Activate(new ActivateEventArgs {User = user});
} }
private bool HandleAttack(ICommonSession session, GridCoordinates coords, EntityUid uid) private bool HandleWideAttack(ICommonSession session, GridCoordinates coords, EntityUid uid)
{ {
// client sanitization // client sanitization
if (!_mapManager.GridExists(coords.GridID)) if (!_mapManager.GridExists(coords.GridID))

View File

@@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Gravity;
using Content.Server.GameObjects.Components.Mobs;
using JetBrains.Annotations;
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.Map;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class GravitySystem: EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
[Dependency] private readonly IRobustRandom _random;
#pragma warning restore 649
private const float GravityKick = 100.0f;
private const uint ShakeTimes = 10;
private Dictionary<GridId, uint> _gridsToShake;
private float internalTimer = 0.0f;
public override void Initialize()
{
EntityQuery = new TypeEntityQuery<GravityGeneratorComponent>();
_gridsToShake = new Dictionary<GridId, uint>();
}
public override void Update(float frameTime)
{
internalTimer += frameTime;
var gridsWithGravity = new List<GridId>();
foreach (var entity in RelevantEntities)
{
var generator = entity.GetComponent<GravityGeneratorComponent>();
if (generator.NeedsUpdate)
{
generator.UpdateState();
}
if (generator.Status == GravityGeneratorStatus.On)
{
gridsWithGravity.Add(entity.Transform.GridID);
}
}
foreach (var grid in _mapManager.GetAllGrids())
{
if (grid.HasGravity && !gridsWithGravity.Contains(grid.Index))
{
grid.HasGravity = false;
ScheduleGridToShake(grid.Index, ShakeTimes);
} else if (!grid.HasGravity && gridsWithGravity.Contains(grid.Index))
{
grid.HasGravity = true;
ScheduleGridToShake(grid.Index, ShakeTimes);
}
}
if (internalTimer > 0.2f)
{
ShakeGrids();
internalTimer = 0.0f;
}
}
private void ScheduleGridToShake(GridId gridId, uint shakeTimes)
{
if (!_gridsToShake.Keys.Contains(gridId))
{
_gridsToShake.Add(gridId, shakeTimes);
}
else
{
_gridsToShake[gridId] = shakeTimes;
}
// Play the gravity sound
foreach (var player in _playerManager.GetAllPlayers())
{
if (player.AttachedEntity == null
|| player.AttachedEntity.Transform.GridID != gridId) continue;
_entitySystemManager.GetEntitySystem<AudioSystem>().Play("/Audio/effects/alert.ogg", player.AttachedEntity);
}
}
private void ShakeGrids()
{
// I have to copy this because C# doesn't allow changing collections while they're
// getting enumerated.
var gridsToShake = new Dictionary<GridId, uint>(_gridsToShake);
foreach (var gridId in _gridsToShake.Keys)
{
if (_gridsToShake[gridId] == 0)
{
gridsToShake.Remove(gridId);
continue;
}
ShakeGrid(gridId);
gridsToShake[gridId] -= 1;
}
_gridsToShake = gridsToShake;
}
private void ShakeGrid(GridId gridId)
{
foreach (var player in _playerManager.GetAllPlayers())
{
if (player.AttachedEntity == null
|| player.AttachedEntity.Transform.GridID != gridId
|| !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent recoil))
{
continue;
}
recoil.Kick(new Vector2(_random.NextFloat(), _random.NextFloat()) * GravityKick);
}
}
}
}

View File

@@ -1,9 +1,14 @@
using System; using System;
using System.Linq;
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.Components.Stack;
using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects; using Content.Server.Interfaces.GameObjects;
using Content.Server.Throw; using Content.Server.Throw;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.Input; using Content.Shared.Input;
using Content.Shared.Interfaces;
using Content.Shared.Physics; using Content.Shared.Physics;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
@@ -19,6 +24,7 @@ using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Physics; using Robust.Shared.Interfaces.Physics;
using Robust.Shared.Interfaces.Timing; using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log; using Robust.Shared.Log;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
@@ -33,6 +39,7 @@ namespace Content.Server.GameObjects.EntitySystems
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager; [Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly IEntitySystemManager _entitySystemManager; [Dependency] private readonly IEntitySystemManager _entitySystemManager;
[Dependency] private readonly IServerNotifyManager _notifyManager;
#pragma warning restore 649 #pragma warning restore 649
private const float ThrowForce = 1.5f; // Throwing force of mobs in Newtons private const float ThrowForce = 1.5f; // Throwing force of mobs in Newtons
@@ -50,6 +57,8 @@ namespace Content.Server.GameObjects.EntitySystems
input.BindMap.BindFunction(ContentKeyFunctions.Drop, new PointerInputCmdHandler(HandleDrop)); input.BindMap.BindFunction(ContentKeyFunctions.Drop, new PointerInputCmdHandler(HandleDrop));
input.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem)); input.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem));
input.BindMap.BindFunction(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem)); input.BindMap.BindFunction(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem));
input.BindMap.BindFunction(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack));
input.BindMap.BindFunction(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt));
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -126,7 +135,7 @@ namespace Content.Server.GameObjects.EntitySystems
var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>(); var interactionSystem = _entitySystemManager.GetEntitySystem<InteractionSystem>();
if(interactionSystem.InRangeUnobstructed(coords.ToMap(_mapManager), ent.Transform.WorldPosition, 0f, ignoredEnt: ent)) if(interactionSystem.InRangeUnobstructed(coords.ToMap(_mapManager), ent.Transform.WorldPosition, ignoredEnt: ent))
if (coords.InRange(_mapManager, ent.Transform.GridPosition, InteractionSystem.InteractionRange)) if (coords.InRange(_mapManager, ent.Transform.GridPosition, InteractionSystem.InteractionRange))
{ {
handsComp.Drop(handsComp.ActiveIndex, coords); handsComp.Drop(handsComp.ActiveIndex, coords);
@@ -190,5 +199,53 @@ namespace Content.Server.GameObjects.EntitySystems
return true; return true;
} }
private void HandleSmartEquipBackpack(ICommonSession session)
{
HandleSmartEquip(session, EquipmentSlotDefines.Slots.BACKPACK);
}
private void HandleSmartEquipBelt(ICommonSession session)
{
HandleSmartEquip(session, EquipmentSlotDefines.Slots.BELT);
}
private void HandleSmartEquip(ICommonSession session, EquipmentSlotDefines.Slots equipementSlot)
{
var plyEnt = ((IPlayerSession) session).AttachedEntity;
if (plyEnt == null || !plyEnt.IsValid())
return;
if (!plyEnt.TryGetComponent(out HandsComponent handsComp) || !plyEnt.TryGetComponent(out InventoryComponent inventoryComp))
return;
if (!inventoryComp.TryGetSlotItem(equipementSlot, out ItemComponent equipmentItem)
|| !equipmentItem.Owner.TryGetComponent<ServerStorageComponent>(out var storageComponent))
{
_notifyManager.PopupMessage(plyEnt, plyEnt, Loc.GetString("You have no {0} to take something out of!", EquipmentSlotDefines.SlotNames[equipementSlot].ToLower()));
return;
}
var heldItem = handsComp.GetHand(handsComp.ActiveIndex)?.Owner;
if (heldItem != null)
{
storageComponent.PlayerInsertEntity(plyEnt);
}
else
{
if (storageComponent.StoredEntities.Count == 0)
{
_notifyManager.PopupMessage(plyEnt, plyEnt, Loc.GetString("There's nothing in your {0} to take out!", EquipmentSlotDefines.SlotNames[equipementSlot].ToLower()));
}
else
{
var lastStoredEntity = Enumerable.Last(storageComponent.StoredEntities);
if (storageComponent.Remove(lastStoredEntity))
handsComp.PutInHandOrDrop(lastStoredEntity.GetComponent<ItemComponent>());
}
}
}
} }
} }

View File

@@ -1,4 +1,6 @@
using Content.Server.GameObjects.Components; using System;
using System.Net;
using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Movement; using Content.Server.GameObjects.Components.Movement;
using Content.Server.GameObjects.Components.Sound; using Content.Server.GameObjects.Components.Sound;
@@ -15,6 +17,7 @@ using Robust.Server.Interfaces.Player;
using Robust.Server.Interfaces.Timing; using Robust.Server.Interfaces.Timing;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Components.Transform; using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input; using Robust.Shared.Input;
@@ -28,6 +31,7 @@ using Robust.Shared.Log;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Physics;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -44,6 +48,7 @@ namespace Content.Server.GameObjects.EntitySystems
[Dependency] private readonly IMapManager _mapManager; [Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly IRobustRandom _robustRandom; [Dependency] private readonly IRobustRandom _robustRandom;
[Dependency] private readonly IConfigurationManager _configurationManager; [Dependency] private readonly IConfigurationManager _configurationManager;
[Dependency] private readonly IEntityManager _entityManager;
#pragma warning restore 649 #pragma warning restore 649
private AudioSystem _audioSystem; private AudioSystem _audioSystem;
@@ -130,13 +135,43 @@ namespace Content.Server.GameObjects.EntitySystems
} }
var mover = entity.GetComponent<IMoverComponent>(); var mover = entity.GetComponent<IMoverComponent>();
var physics = entity.GetComponent<PhysicsComponent>(); var physics = entity.GetComponent<PhysicsComponent>();
if (entity.TryGetComponent<CollidableComponent>(out var collider))
UpdateKinematics(entity.Transform, mover, physics); {
UpdateKinematics(entity.Transform, mover, physics, collider);
}
else
{
UpdateKinematics(entity.Transform, mover, physics);
}
} }
} }
private void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, PhysicsComponent physics) private void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, PhysicsComponent physics, CollidableComponent collider = null)
{ {
bool weightless = false;
var tile = _mapManager.GetGrid(transform.GridID).GetTileRef(transform.GridPosition).Tile;
if ((!_mapManager.GetGrid(transform.GridID).HasGravity || tile.IsEmpty) && collider != null)
{
weightless = true;
// No gravity: is our entity touching anything?
var touching = false;
foreach (var entity in _entityManager.GetEntitiesInRange(transform.Owner, mover.GrabRange, true))
{
if (entity.TryGetComponent<CollidableComponent>(out var otherCollider))
{
if (otherCollider.Owner == transform.Owner) continue; // Don't try to push off of yourself!
touching |= ((collider.CollisionMask & otherCollider.CollisionLayer) != 0x0
|| (otherCollider.CollisionMask & collider.CollisionLayer) != 0x0) // Ensure collision
&& !entity.HasComponent<ItemComponent>(); // This can't be an item
}
}
if (!touching)
{
return;
}
}
if (mover.VelocityDir.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner)) if (mover.VelocityDir.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner))
{ {
if (physics.LinearVelocity != Vector2.Zero) if (physics.LinearVelocity != Vector2.Zero)
@@ -145,6 +180,13 @@ namespace Content.Server.GameObjects.EntitySystems
} }
else else
{ {
if (weightless)
{
physics.LinearVelocity = mover.VelocityDir * mover.CurrentPushSpeed;
transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle();
return;
}
physics.LinearVelocity = mover.VelocityDir * (mover.Sprinting ? mover.CurrentSprintSpeed : mover.CurrentWalkSpeed); physics.LinearVelocity = mover.VelocityDir * (mover.Sprinting ? mover.CurrentSprintSpeed : mover.CurrentWalkSpeed);
transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle(); transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle();

View File

@@ -1,11 +1,14 @@
namespace Content.Server.GameTicking using System.Collections.Generic;
using Robust.Server.Interfaces.Player;
namespace Content.Server.GameTicking
{ {
/// <summary> /// <summary>
/// A round-start setup preset, such as which antagonists to spawn. /// A round-start setup preset, such as which antagonists to spawn.
/// </summary> /// </summary>
public abstract class GamePreset public abstract class GamePreset
{ {
public abstract void Start(); public abstract bool Start(IReadOnlyList<IPlayerSession> players);
public virtual string ModeTitle => "Sandbox"; public virtual string ModeTitle => "Sandbox";
public virtual string Description => "Secret!"; public virtual string Description => "Secret!";
} }

View File

@@ -1,5 +1,7 @@
using Content.Server.GameTicking.GameRules; using System.Collections.Generic;
using Content.Server.GameTicking.GameRules;
using Content.Server.Interfaces.GameTicking; using Content.Server.Interfaces.GameTicking;
using Robust.Server.Interfaces.Player;
using Robust.Shared.IoC; using Robust.Shared.IoC;
namespace Content.Server.GameTicking.GamePresets namespace Content.Server.GameTicking.GamePresets
@@ -10,9 +12,10 @@ namespace Content.Server.GameTicking.GamePresets
[Dependency] private readonly IGameTicker _gameTicker; [Dependency] private readonly IGameTicker _gameTicker;
#pragma warning restore 649 #pragma warning restore 649
public override void Start() public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers)
{ {
_gameTicker.AddGameRule<RuleDeathMatch>(); _gameTicker.AddGameRule<RuleDeathMatch>();
return true;
} }
public override string ModeTitle => "Deathmatch"; public override string ModeTitle => "Deathmatch";

View File

@@ -1,4 +1,6 @@
using Content.Server.Sandbox; using System.Collections.Generic;
using Content.Server.Sandbox;
using Robust.Server.Interfaces.Player;
using Robust.Shared.IoC; using Robust.Shared.IoC;
namespace Content.Server.GameTicking.GamePresets namespace Content.Server.GameTicking.GamePresets
@@ -9,9 +11,10 @@ namespace Content.Server.GameTicking.GamePresets
[Dependency] private readonly ISandboxManager _sandboxManager; [Dependency] private readonly ISandboxManager _sandboxManager;
#pragma warning restore 649 #pragma warning restore 649
public override void Start() public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers)
{ {
_sandboxManager.IsSandboxEnabled = true; _sandboxManager.IsSandboxEnabled = true;
return true;
} }
public override string ModeTitle => "Sandbox"; public override string ModeTitle => "Sandbox";

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameTicking.GameRules;
using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameTicking;
using Content.Server.Mobs.Roles;
using Content.Server.Players;
using Content.Server.Sandbox;
using NFluidsynth;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Random;
using Logger = Robust.Shared.Log.Logger;
namespace Content.Server.GameTicking.GamePresets
{
public class PresetSuspicion : GamePreset
{
#pragma warning disable 649
[Dependency] private readonly ISandboxManager _sandboxManager;
[Dependency] private readonly IChatManager _chatManager;
[Dependency] private readonly IGameTicker _gameTicker;
[Dependency] private readonly IRobustRandom _random;
#pragma warning restore 649
public int MinPlayers { get; set; } = 5;
public int MinTraitors { get; set; } = 2;
public int PlayersPerTraitor { get; set; } = 5;
public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers)
{
if (readyPlayers.Count < MinPlayers)
{
_chatManager.DispatchServerAnnouncement($"Not enough players readied up for the game! There were {readyPlayers.Count} players readied up out of {MinPlayers} needed.");
return false;
}
var list = new List<IPlayerSession>(readyPlayers);
var numTraitors = Math.Max(readyPlayers.Count() % PlayersPerTraitor, MinTraitors);
for (var i = 0; i < numTraitors; i++)
{
var traitor = _random.PickAndTake(list);
var mind = traitor.Data.ContentData().Mind;
mind.AddRole(new SuspicionTraitorRole(mind));
}
foreach (var player in list)
{
var mind = player.Data.ContentData().Mind;
mind.AddRole(new SuspicionInnocentRole(mind));
}
_gameTicker.AddGameRule<RuleSuspicion>();
return true;
}
public override string ModeTitle => "Suspicion";
public override string Description => "Suspicion on the Space Station. There are traitors on board... Can you kill them before they kill you?";
}
}

View File

@@ -0,0 +1,122 @@
using System;
using System.Threading;
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Observer;
using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameTicking;
using Content.Server.Mobs.Roles;
using Content.Server.Players;
using NFluidsynth;
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.IoC;
using Logger = Robust.Shared.Log.Logger;
using Timer = Robust.Shared.Timers.Timer;
namespace Content.Server.GameTicking.GameRules
{
/// <summary>
/// Simple GameRule that will do a free-for-all death match.
/// Kill everybody else to win.
/// </summary>
public sealed class RuleSuspicion : GameRule, IEntityEventSubscriber
{
private static readonly TimeSpan DeadCheckDelay = TimeSpan.FromSeconds(1);
#pragma warning disable 649
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IChatManager _chatManager;
[Dependency] private readonly IEntityManager _entityManager;
[Dependency] private readonly IGameTicker _gameTicker;
#pragma warning restore 649
private readonly CancellationTokenSource _checkTimerCancel = new CancellationTokenSource();
public override void Added()
{
_chatManager.DispatchServerAnnouncement("There are traitors on the station! Find them, and kill them!");
_entityManager.EventBus.SubscribeEvent<MobDamageStateChangedMessage>(EventSource.Local, this, _onMobDamageStateChanged);
Timer.SpawnRepeating(DeadCheckDelay, _checkWinConditions, _checkTimerCancel.Token);
}
private void _onMobDamageStateChanged(MobDamageStateChangedMessage message)
{
var owner = message.Species.Owner;
if (!(message.Species.CurrentDamageState is DeadState))
return;
if (!owner.TryGetComponent<MindComponent>(out var mind))
return;
if (!mind.HasMind)
return;
message.Species.Owner.Description +=
mind.Mind.HasRole<SuspicionTraitorRole>() ? "\nThey were a traitor!" : "\nThey were an innocent!";
}
public override void Removed()
{
base.Removed();
_checkTimerCancel.Cancel();
}
private void _checkWinConditions()
{
var traitorsAlive = 0;
var innocentsAlive = 0;
foreach (var playerSession in _playerManager.GetAllPlayers())
{
if (playerSession.AttachedEntity == null
|| !playerSession.AttachedEntity.TryGetComponent(out SpeciesComponent species))
{
continue;
}
if (!species.CurrentDamageState.IsConscious)
{
continue;
}
if (playerSession.ContentData().Mind.HasRole<SuspicionTraitorRole>())
traitorsAlive++;
else
innocentsAlive++;
}
if ((innocentsAlive + traitorsAlive) == 0)
{
_chatManager.DispatchServerAnnouncement("Everybody is dead, it's a stalemate!");
EndRound();
}
else if (traitorsAlive == 0)
{
_chatManager.DispatchServerAnnouncement("The traitors are dead! The innocents win.");
EndRound();
}
else if (innocentsAlive == 0)
{
_chatManager.DispatchServerAnnouncement("The innocents are dead! The traitors win.");
EndRound();
}
}
private void EndRound()
{
_gameTicker.EndRound();
_chatManager.DispatchServerAnnouncement($"Restarting in 10 seconds.");
_checkTimerCancel.Cancel();
Timer.Spawn(TimeSpan.FromSeconds(10), () => _gameTicker.RestartRound());
}
}
}

View File

@@ -104,7 +104,8 @@ namespace Content.Server.GameTicking
_configurationManager.RegisterCVar("game.lobbyenabled", false, CVar.ARCHIVE); _configurationManager.RegisterCVar("game.lobbyenabled", false, CVar.ARCHIVE);
_configurationManager.RegisterCVar("game.lobbyduration", 20, CVar.ARCHIVE); _configurationManager.RegisterCVar("game.lobbyduration", 20, CVar.ARCHIVE);
_configurationManager.RegisterCVar("game.defaultpreset", "Sandbox", CVar.ARCHIVE); _configurationManager.RegisterCVar("game.defaultpreset", "Suspicion", CVar.ARCHIVE);
_configurationManager.RegisterCVar("game.fallbackpreset", "Sandbox", CVar.ARCHIVE);
_playerManager.PlayerStatusChanged += _handlePlayerStatusChanged; _playerManager.PlayerStatusChanged += _handlePlayerStatusChanged;
@@ -181,11 +182,6 @@ namespace Content.Server.GameTicking
SendServerMessage("The round is starting now..."); SendServerMessage("The round is starting now...");
RunLevel = GameRunLevel.InRound;
var preset = MakeGamePreset();
preset.Start();
List<IPlayerSession> readyPlayers; List<IPlayerSession> readyPlayers;
if (LobbyEnabled) if (LobbyEnabled)
{ {
@@ -196,6 +192,8 @@ namespace Content.Server.GameTicking
readyPlayers = _playersInLobby.Keys.ToList(); readyPlayers = _playersInLobby.Keys.ToList();
} }
RunLevel = GameRunLevel.InRound;
// Get the profiles for each player for easier lookup. // Get the profiles for each player for easier lookup.
var profiles = readyPlayers.ToDictionary(p => p, GetPlayerProfile); var profiles = readyPlayers.ToDictionary(p => p, GetPlayerProfile);
@@ -222,6 +220,18 @@ namespace Content.Server.GameTicking
SpawnPlayer(player, job, false); SpawnPlayer(player, job, false);
} }
// Time to start the preset.
var preset = MakeGamePreset();
if (!preset.Start(assignedJobs.Keys.ToList()))
{
SetStartPreset(_configurationManager.GetCVar<string>("game.fallbackpreset"));
var newPreset = MakeGamePreset();
_chatManager.DispatchServerAnnouncement($"Failed to start {preset.ModeTitle} mode! Defaulting to {newPreset.ModeTitle}...");
if(!newPreset.Start(readyPlayers))
throw new ApplicationException("Fallback preset failed to start!");
}
_roundStartTimeSpan = IoCManager.Resolve<IGameTiming>().RealTime; _roundStartTimeSpan = IoCManager.Resolve<IGameTiming>().RealTime;
_sendStatusToAll(); _sendStatusToAll();
} }
@@ -255,15 +265,16 @@ namespace Content.Server.GameTicking
var listOfPlayerInfo = new List<RoundEndPlayerInfo>(); var listOfPlayerInfo = new List<RoundEndPlayerInfo>();
foreach(var ply in _playerManager.GetAllPlayers().OrderBy(p => p.Name)) foreach(var ply in _playerManager.GetAllPlayers().OrderBy(p => p.Name))
{ {
if(ply.AttachedEntity.TryGetComponent<MindComponent>(out var mindComponent) var mind = ply.ContentData().Mind;
&& mindComponent.HasMind) if(mind != null)
{ {
var antag = mind.AllRoles.Any(role => role.Antag);
var playerEndRoundInfo = new RoundEndPlayerInfo() var playerEndRoundInfo = new RoundEndPlayerInfo()
{ {
PlayerOOCName = ply.Name, PlayerOOCName = ply.Name,
PlayerICName = mindComponent.Mind.CurrentEntity.Name, PlayerICName = mind.CurrentEntity.Name,
Role = mindComponent.Mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("Unkown"), Role = antag ? mind.AllRoles.First(role => role.Antag).Name : mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("Unkown"),
Antag = false Antag = antag
}; };
listOfPlayerInfo.Add(playerEndRoundInfo); listOfPlayerInfo.Add(playerEndRoundInfo);
} }
@@ -339,6 +350,7 @@ namespace Content.Server.GameTicking
{ {
"Sandbox" => typeof(PresetSandbox), "Sandbox" => typeof(PresetSandbox),
"DeathMatch" => typeof(PresetDeathMatch), "DeathMatch" => typeof(PresetDeathMatch),
"Suspicion" => typeof(PresetSuspicion),
_ => throw new NotSupportedException() _ => throw new NotSupportedException()
}); });

View File

@@ -19,6 +19,17 @@ namespace Content.Server.Interfaces.GameObjects.Components.Movement
/// </summary> /// </summary>
float CurrentSprintSpeed { get; } float CurrentSprintSpeed { get; }
/// <summary>
/// The movement speed (m/s) of the entity when it pushes off of a solid object in zero gravity.
/// </summary>
float CurrentPushSpeed { get; }
/// <summary>
/// How far an entity can reach (in meters) to grab hold of a solid object in zero gravity.
/// </summary>
float GrabRange { get; }
/// <summary> /// <summary>
/// Is the entity Sprinting (running)? /// Is the entity Sprinting (running)?
/// </summary> /// </summary>

View File

@@ -1,12 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Players; using Content.Server.Players;
using Robust.Server.Interfaces.GameObjects; using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.Mobs namespace Content.Server.Mobs
@@ -130,6 +133,13 @@ namespace Content.Server.Mobs
_roles.Remove(role); _roles.Remove(role);
} }
public bool HasRole<T>() where T : Role
{
var t = typeof(T);
return _roles.Any(role => role.GetType() == t);
}
/// <summary> /// <summary>
/// Transfer this mind's control over to a new entity. /// Transfer this mind's control over to a new entity.
/// </summary> /// </summary>

View File

@@ -1,6 +1,9 @@
// Hey look, // Hey look,
// Antag Datums. // Antag Datums.
using Content.Server.GameObjects.EntitySystems;
using Robust.Shared.Utility;
namespace Content.Server.Mobs namespace Content.Server.Mobs
{ {
/// <summary> /// <summary>
@@ -20,6 +23,11 @@ namespace Content.Server.Mobs
/// </summary> /// </summary>
public abstract string Name { get; } public abstract string Name { get; }
/// <summary>
/// Whether this role should be considered antagonistic or not.
/// </summary>
public abstract bool Antag { get; }
protected Role(Mind mind) protected Role(Mind mind)
{ {
Mind = mind; Mind = mind;

View File

@@ -11,8 +11,9 @@ namespace Content.Server.Mobs.Roles
public JobPrototype Prototype { get; } public JobPrototype Prototype { get; }
public override string Name { get; } public override string Name { get; }
public override bool Antag => false;
public String StartingGear => Prototype.StartingGear; public string StartingGear => Prototype.StartingGear;
public Job(Mind mind, JobPrototype jobPrototype) : base(mind) public Job(Mind mind, JobPrototype jobPrototype) : base(mind)
{ {
@@ -25,9 +26,7 @@ namespace Content.Server.Mobs.Roles
base.Greet(); base.Greet();
var chat = IoCManager.Resolve<IChatManager>(); var chat = IoCManager.Resolve<IChatManager>();
chat.DispatchServerMessage( chat.DispatchServerMessage(Mind.Session, $"You're a new {Name}. Do your best!");
Mind.Session,
String.Format("You're a new {0}. Do your best!", Name));
} }
} }

View File

@@ -0,0 +1,25 @@
using Content.Server.GameObjects;
using Content.Server.Interfaces.Chat;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
namespace Content.Server.Mobs.Roles
{
public class SuspicionInnocentRole : Role
{
public SuspicionInnocentRole(Mind mind) : base(mind)
{
}
public override string Name => "Innocent";
public override bool Antag => false;
public override void Greet()
{
base.Greet();
var chat = IoCManager.Resolve<IChatManager>();
chat.DispatchServerMessage(Mind.Session, "You're an innocent!");
}
}
}

View File

@@ -0,0 +1,25 @@
using Content.Server.GameObjects;
using Content.Server.Interfaces.Chat;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
namespace Content.Server.Mobs.Roles
{
public sealed class SuspicionTraitorRole : Role
{
public SuspicionTraitorRole(Mind mind) : base(mind)
{
}
public override string Name => "Traitor";
public override bool Antag => true;
public override void Greet()
{
base.Greet();
var chat = IoCManager.Resolve<IChatManager>();
chat.DispatchServerMessage(Mind.Session, "You're a traitor!");
}
}
}

View File

@@ -1,24 +0,0 @@
using Content.Server.Interfaces.Chat;
using Robust.Shared.IoC;
namespace Content.Server.Mobs.Roles
{
public sealed class Traitor : Role
{
public Traitor(Mind mind) : base(mind)
{
}
public override string Name => "Traitor";
public override void Greet()
{
base.Greet();
var chat = IoCManager.Resolve<IChatManager>();
chat.DispatchServerMessage(
Mind.Session,
"You're a traitor. Go fuck something up. Or something. I don't care to be honest.");
}
}
}

View File

@@ -8,6 +8,7 @@ using Content.Server.Preferences;
using Content.Server.Sandbox; using Content.Server.Sandbox;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Kitchen;
using Content.Shared.Interfaces; using Content.Shared.Interfaces;
using Content.Shared.Interfaces.Chemistry; using Content.Shared.Interfaces.Chemistry;
using Robust.Shared.IoC; using Robust.Shared.IoC;
@@ -28,6 +29,7 @@ namespace Content.Server
IoCManager.Register<ICargoOrderDataManager, CargoOrderDataManager>(); IoCManager.Register<ICargoOrderDataManager, CargoOrderDataManager>();
IoCManager.Register<IModuleManager, ServerModuleManager>(); IoCManager.Register<IModuleManager, ServerModuleManager>();
IoCManager.Register<IServerPreferencesManager, ServerPreferencesManager>(); IoCManager.Register<IServerPreferencesManager, ServerPreferencesManager>();
IoCManager.Register<RecipeManager, RecipeManager>();
} }
} }
} }

View File

@@ -65,6 +65,14 @@ namespace Content.Server.Throw
var spd = a / (1f / timing.TickRate); // acceleration is applied in 1 tick instead of 1 second, scale appropriately var spd = a / (1f / timing.TickRate); // acceleration is applied in 1 tick instead of 1 second, scale appropriately
physComp.LinearVelocity = angle.ToVec() * spd; physComp.LinearVelocity = angle.ToVec() * spd;
if (throwSourceEnt != null)
{
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);
}
} }
} }
} }

View File

@@ -0,0 +1,22 @@
using System;
using Content.Shared.GameObjects.Components.Sound;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Random;
namespace Content.Shared.Audio
{
public static class AudioHelpers{
/// <summary>
/// Returns a random pitch.
/// </summary>
public static AudioParams WithVariation(float amplitude)
{
var scale = (float)(IoCManager.Resolve<IRobustRandom>().NextGaussian(1, amplitude));
return AudioParams.Default.WithPitchScale(scale);
}
}
}

View File

@@ -1,23 +1,36 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using Content.Shared.Maps; using Content.Shared.Maps;
using Robust.Shared.ContentPack; using Robust.Shared.ContentPack;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Shared namespace Content.Shared
{ {
public class EntryPoint : GameShared public class EntryPoint : GameShared
{ {
// If you want to change your codebase's language, do it here.
private const string Culture = "en-US";
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager; [Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
[Dependency] private readonly ILocalizationManager _localizationManager;
#pragma warning restore 649 #pragma warning restore 649
public override void PreInit()
{
IoCManager.InjectDependencies(this);
// Default to en-US.
_localizationManager.LoadCulture(new CultureInfo(Culture));
}
public override void Init() public override void Init()
{ {
IoCManager.InjectDependencies(this);
} }
public override void PostInit() public override void PostInit()

View File

@@ -0,0 +1,57 @@
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Gravity
{
public class SharedGravityGeneratorComponent: Component
{
public override string Name => "GravityGenerator";
public override uint? NetID => ContentNetIDs.GRAVITY_GENERATOR;
/// <summary>
/// Sent to the server to set whether the generator should be on or off
/// </summary>
[Serializable, NetSerializable]
public class SwitchGeneratorMessage : BoundUserInterfaceMessage
{
public bool On;
public SwitchGeneratorMessage(bool on)
{
On = on;
}
}
/// <summary>
/// Sent to the server when requesting the status of the generator
/// </summary>
[Serializable, NetSerializable]
public class GeneratorStatusRequestMessage : BoundUserInterfaceMessage
{
public GeneratorStatusRequestMessage()
{
}
}
[Serializable, NetSerializable]
public class GeneratorState : BoundUserInterfaceState
{
public bool On;
public GeneratorState(bool on)
{
On = on;
}
}
[Serializable, NetSerializable]
public enum GravityGeneratorUiKey
{
Key
}
}
}

View File

@@ -42,5 +42,7 @@
public const uint PAPER = 1037; public const uint PAPER = 1037;
public const uint REAGENT_INJECTOR = 1038; public const uint REAGENT_INJECTOR = 1038;
public const uint GHOST = 1039; public const uint GHOST = 1039;
public const uint MICROWAVE = 1040;
public const uint GRAVITY_GENERATOR = 1041;
} }
} }

View File

@@ -51,13 +51,17 @@ namespace Content.Server.GameObjects.EntitySystems
var rayResults = _physicsManager.IntersectRayWithPredicate(coords.MapId, ray, dir.Length, predicate, true); var rayResults = _physicsManager.IntersectRayWithPredicate(coords.MapId, ray, dir.Length, predicate, true);
if(!rayResults.DidHitObject || (insideBlockerValid && rayResults.DidHitObject && (rayResults.HitPos - otherCoords).Length < 1f)) if(!rayResults.DidHitObject || (insideBlockerValid && rayResults.DidHitObject && (rayResults.HitPos - otherCoords).Length < 1f))
{ {
_mapManager.TryFindGridAt(coords, out var mapGrid);
var srcPos = mapGrid.MapToGrid(coords); if (_mapManager.TryFindGridAt(coords, out var mapGrid) && mapGrid != null)
var destPos = new GridCoordinates(otherCoords, mapGrid);
if (srcPos.InRange(_mapManager, destPos, range))
{ {
return true; var srcPos = mapGrid.MapToGrid(coords);
var destPos = new GridCoordinates(otherCoords, mapGrid);
if (srcPos.InRange(_mapManager, destPos, range))
{
return true;
}
} }
} }
return false; return false;
} }

View File

@@ -5,8 +5,7 @@ namespace Content.Shared.Input
[KeyFunctions] [KeyFunctions]
public static class ContentKeyFunctions public static class ContentKeyFunctions
{ {
public static readonly BoundKeyFunction UseOrAttack = "UseOrAttack"; public static readonly BoundKeyFunction WideAttack = "WideAttack";
public static readonly BoundKeyFunction Attack = "Attack";
public static readonly BoundKeyFunction ActivateItemInHand = "ActivateItemInHand"; public static readonly BoundKeyFunction ActivateItemInHand = "ActivateItemInHand";
public static readonly BoundKeyFunction ActivateItemInWorld = "ActivateItemInWorld"; // default action on world entity public static readonly BoundKeyFunction ActivateItemInWorld = "ActivateItemInWorld"; // default action on world entity
public static readonly BoundKeyFunction Drop = "Drop"; public static readonly BoundKeyFunction Drop = "Drop";
@@ -16,6 +15,8 @@ namespace Content.Shared.Input
public static readonly BoundKeyFunction OpenContextMenu = "OpenContextMenu"; public static readonly BoundKeyFunction OpenContextMenu = "OpenContextMenu";
public static readonly BoundKeyFunction OpenCraftingMenu = "OpenCraftingMenu"; public static readonly BoundKeyFunction OpenCraftingMenu = "OpenCraftingMenu";
public static readonly BoundKeyFunction OpenInventoryMenu = "OpenInventoryMenu"; public static readonly BoundKeyFunction OpenInventoryMenu = "OpenInventoryMenu";
public static readonly BoundKeyFunction SmartEquipBackpack = "SmartEquipBackpack";
public static readonly BoundKeyFunction SmartEquipBelt = "SmartEquipBelt";
public static readonly BoundKeyFunction OpenTutorial = "OpenTutorial"; public static readonly BoundKeyFunction OpenTutorial = "OpenTutorial";
public static readonly BoundKeyFunction SwapHands = "SwapHands"; public static readonly BoundKeyFunction SwapHands = "SwapHands";
public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand"; public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand";
@@ -24,5 +25,7 @@ namespace Content.Shared.Input
public static readonly BoundKeyFunction OpenEntitySpawnWindow = "OpenEntitySpawnWindow"; public static readonly BoundKeyFunction OpenEntitySpawnWindow = "OpenEntitySpawnWindow";
public static readonly BoundKeyFunction OpenSandboxWindow = "OpenSandboxWindow"; public static readonly BoundKeyFunction OpenSandboxWindow = "OpenSandboxWindow";
public static readonly BoundKeyFunction OpenTileSpawnWindow = "OpenTileSpawnWindow"; public static readonly BoundKeyFunction OpenTileSpawnWindow = "OpenTileSpawnWindow";
public static readonly BoundKeyFunction TakeScreenshot = "TakeScreenshot";
public static readonly BoundKeyFunction TakeScreenshotNoUI = "TakeScreenshotNoUI";
} }
} }

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using Content.Shared.Prototypes.Kitchen;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Shared.Kitchen
{
public class RecipeManager
{
#pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager;
#pragma warning restore 649
public List<FoodRecipePrototype> Recipes { get; private set; }
public void Initialize()
{
Recipes = new List<FoodRecipePrototype>();
foreach (var item in _prototypeManager.EnumeratePrototypes<FoodRecipePrototype>())
{
Recipes.Add(item);
}
Recipes.Sort(new RecipeComparer());
}
private class RecipeComparer : Comparer<FoodRecipePrototype>
{
public override int Compare(FoodRecipePrototype x, FoodRecipePrototype y)
{
if (x == null || y == null)
{
return 0;
}
return -x.IngredientsReagents.Count.CompareTo(y.IngredientsReagents.Count);
}
}
}
}

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.GameObjects.Components.UserInterface;
namespace Content.Shared.Kitchen
{
public class SharedMicrowaveComponent : Component
{
public override string Name => "Microwave";
public override uint? NetID => ContentNetIDs.MICROWAVE;
[Serializable, NetSerializable]
public class MicrowaveStartCookMessage : BoundUserInterfaceMessage
{
public MicrowaveStartCookMessage()
{
}
}
[Serializable, NetSerializable]
public class MicrowaveEjectMessage : BoundUserInterfaceMessage
{
public MicrowaveEjectMessage()
{
}
}
[Serializable, NetSerializable]
public class MicrowaveEjectSolidIndexedMessage : BoundUserInterfaceMessage
{
public EntityUid EntityID;
public MicrowaveEjectSolidIndexedMessage(EntityUid entityID)
{
EntityID = entityID;
}
}
[Serializable, NetSerializable]
public class MicrowaveVaporizeReagentIndexedMessage : BoundUserInterfaceMessage
{
public Solution.ReagentQuantity ReagentQuantity;
public MicrowaveVaporizeReagentIndexedMessage(Solution.ReagentQuantity reagentQuantity)
{
ReagentQuantity = reagentQuantity;
}
}
[Serializable, NetSerializable]
public class MicrowaveSelectCookTimeMessage : BoundUserInterfaceMessage
{
public uint newCookTime;
public MicrowaveSelectCookTimeMessage(uint inputTime)
{
newCookTime = inputTime;
}
}
}
[NetSerializable, Serializable]
public class MicrowaveUpdateUserInterfaceState : BoundUserInterfaceState
{
public readonly IReadOnlyList<Solution.ReagentQuantity> ReagentsReagents;
public readonly List<EntityUid> ContainedSolids;
public MicrowaveUpdateUserInterfaceState(IReadOnlyList<Solution.ReagentQuantity> reagents, List<EntityUid> solids)
{
ReagentsReagents = reagents;
ContainedSolids = solids;
}
}
[Serializable, NetSerializable]
public enum MicrowaveVisualState
{
Idle,
Cooking
}
[NetSerializable, Serializable]
public enum MicrowaveUiKey
{
Key
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Shared.Prototypes.Kitchen
{
/// <summary>
/// A recipe for space microwaves.
/// </summary>
[Prototype("microwaveMealRecipe")]
public class FoodRecipePrototype : IPrototype, IIndexedPrototype
{
private string _id;
private string _name;
private string _result;
private int _cookTime;
private Dictionary<string, int> _ingsReagents;
private Dictionary<string, int> _ingsSolids;
public string Name => Loc.GetString(_name);
public string ID => _id;
public string Result => _result;
public int CookTime => _cookTime;
public IReadOnlyDictionary<string, int> IngredientsReagents => _ingsReagents;
public IReadOnlyDictionary<string, int> IngredientsSolids => _ingsSolids;
public void LoadFrom(YamlMappingNode mapping)
{
var serializer = YamlObjectSerializer.NewReader(mapping);
serializer.DataField(ref _id, "id", string.Empty);
serializer.DataField(ref _name, "name", string.Empty);
serializer.DataField(ref _result, "result", string.Empty);
serializer.DataField(ref _ingsReagents, "reagents", new Dictionary<string, int>());
serializer.DataField(ref _ingsSolids, "solids", new Dictionary<string, int>());
serializer.DataField(ref _cookTime, "time", 5);
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -134,3 +134,4 @@
- gc_mode - gc_mode
CanViewVar: true CanViewVar: true
CanAdminPlace: true CanAdminPlace: true
CanScript: true

View File

@@ -0,0 +1,19 @@
# Example Dutch translations
- msgid: Wrench
msgstr: Moersleutel
- msgid: Welding Tool
msgstr: Lasapparaat
- msgid: Crowbar
msgstr: Koevoet
- msgid: Screwdriver
msgstr: Schroevendraaier
- msgid: Wirecutters
msgstr: Draadtang
- msgid: Multitool
msgstr: Multi Tool # This is what google translate gives me idk.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
- type: entity
id: GravityGenerator
name: Gravity Generator
description: It's what keeps you to the floor.
components:
- type: Sprite
sprite: Buildings/gravity_generator.rsi
layers:
- state: on
- sprite: Buildings/gravity_generator_core.rsi
state: activated
shader: unshaded
- type: Icon
sprite: Buildings/gravity_generator.rsi
state: on
- type: SnapGrid
offset: Center
- type: PowerDevice
load: 500
- type: Collidable
shapes:
- !type:PhysShapeAabb
bounds: "-1.5,-1.5,1.5,1.5"
layer: 15
- type: Clickable
- type: InteractionOutline
- type: Damageable
- type: Breakable
threshold: 150
- type: GravityGenerator
- type: UserInterface
interfaces:
- key: enum.GravityGeneratorUiKey.Key
type: GravityGeneratorBoundUserInterface
placement:
mode: AlignTileAny

View File

@@ -36,7 +36,6 @@
- type: Appearance - type: Appearance
visuals: visuals:
- type: MedicalScannerVisualizer2D - type: MedicalScannerVisualizer2D
- type: PowerDevice
- type: UserInterface - type: UserInterface
interfaces: interfaces:
- key: enum.MedicalScannerUiKey.Key - key: enum.MedicalScannerUiKey.Key

View File

@@ -21,6 +21,7 @@
state: utilitybelt state: utilitybelt
- type: Clothing - type: Clothing
Size: 50 Size: 50
QuickEquip: false
sprite: Clothing/belt_utility.rsi sprite: Clothing/belt_utility.rsi
- type: Storage - type: Storage
Capacity: 40 # Full tool loadout is 35, plus an extra Capacity: 40 # Full tool loadout is 35, plus an extra

View File

@@ -1482,19 +1482,21 @@
- type: Icon - type: Icon
sprite: Objects/Food/loadedbakedpotato.rsi sprite: Objects/Food/loadedbakedpotato.rsi
#- type: entity - type: entity
# parent: FoodBase parent: FoodBase
# id: FoodMeat id: FoodMeat
# name: Meat name: Meat
# description: '' description: A slab of meat.
# components: components:
# - type: Food - type: Food
# uses: 1 contents:
# restore_amount: 1 reagents:
# - type: Sprite - ReagentId: chem.Nutriment
# sprite: Objects/Food/meat.rsi Quantity: 5
# - type: Icon - type: Sprite
# sprite: Objects/Food/meat.rsi sprite: Objects/Food/meat.rsi
- type: Icon
sprite: Objects/Food/meat.rsi
- type: entity - type: entity
parent: FoodBase parent: FoodBase
@@ -2835,19 +2837,21 @@
- type: Icon - type: Icon
sprite: Objects/Food/xenobreadslice.rsi sprite: Objects/Food/xenobreadslice.rsi
#- type: entity - type: entity
# parent: FoodBase parent: FoodBase
# id: FoodXenomeat id: FoodXenomeat
# name: Xenomeat name: Xenomeat
# description: '' description: Yum.
# components: components:
# - type: Food - type: Food
# uses: 1 contents:
# restore_amount: 1 reagents:
# - type: Sprite - ReagentId: chem.Nutriment
# sprite: Objects/Food/xenomeat.rsi Quantity: 20
# - type: Icon - type: Sprite
# sprite: Objects/Food/xenomeat.rsi sprite: Objects/Food/xenomeat.rsi
- type: Icon
sprite: Objects/Food/xenomeat.rsi
- type: entity - type: entity
parent: FoodBase parent: FoodBase
@@ -2865,3 +2869,20 @@
sprite: Objects/Food/xenomeatpie.rsi sprite: Objects/Food/xenomeatpie.rsi
- type: Icon - type: Icon
sprite: Objects/Food/xenomeatpie.rsi sprite: Objects/Food/xenomeatpie.rsi
- type: entity
parent: FoodBase
id: DisgustingSweptSoup
name: Salty Sweet Miso Cola Soup
description: Jesus christ.
components:
- type: Food
contents:
reagents:
- ReagentId: chem.Bleach
Quantity: 15
spawn_on_finish: TrashSnackBowl
- type: Sprite
sprite: Objects/Food/stew.rsi
- type: Icon
sprite: Objects/Food/stew.rsi

View File

@@ -0,0 +1,22 @@
- type: entity
parent: BaseItem
id: ReagentContainerFlour
name: Flour
description: Not to be confused with flower.
components:
- type: Solution
maxVol: 50
contents:
reagents:
- ReagentId: chem.Flour
Quantity: 50
- type: Pourable
transferAmount: 5
- type: Drink
despawn_empty: true
- type: Sprite
sprite: Objects/Food/flour.rsi
state: icon
- type: Icon
sprite: Objects/Food/flour.rsi
state: icon

View File

@@ -23,6 +23,18 @@
- type: Icon - type: Icon
texture: Objects/Instruments/musician.rsi/h_synthesizer.png texture: Objects/Instruments/musician.rsi/h_synthesizer.png
- type: entity
name: Acoustic Guitar
parent: BaseHandheldInstrument
id: AcousticGuitarInstrument
components:
- type: Instrument
program: 25
- type: Sprite
texture: Objects/Instruments/musician.rsi/guitar.png
- type: Icon
texture: Objects/Instruments/musician.rsi/guitar.png
- type: entity - type: entity
name: Violin name: Violin
parent: BaseHandheldInstrument parent: BaseHandheldInstrument

View File

@@ -20,6 +20,7 @@
- type: Sound - type: Sound
- type: EmitSoundOnUse - type: EmitSoundOnUse
sound: /Audio/items/bikehorn.ogg sound: /Audio/items/bikehorn.ogg
variation: 0.2
- type: UseDelay - type: UseDelay
delay: 0.5 delay: 0.5

View File

@@ -42,6 +42,3 @@
- type: Item - type: Item
Size: 24 Size: 24
sprite: Objects/Guns/SMGs/c20r.rsi sprite: Objects/Guns/SMGs/c20r.rsi
- type: Item
Size: 24
sprite: Objects/Guns/SMGs/wt550.rsi

View File

@@ -34,7 +34,6 @@
drawdepth: Objects drawdepth: Objects
- type: Icon - type: Icon
texture: Objects/Janitorial/mopbucket.png texture: Objects/Janitorial/mopbucket.png
- type: Clickable
- type: InteractionOutline - type: InteractionOutline
- type: Bucket - type: Bucket
- type: Sound - type: Sound
@@ -51,7 +50,6 @@
- type: Physics - type: Physics
mass: 5 mass: 5
Anchored: false Anchored: false
- type: Sound
- type: entity - type: entity
parent: BaseItem parent: BaseItem

View File

@@ -0,0 +1,40 @@
- type: entity
id: KitchenMicrowave
name: Microwave
description: It's magic.
components:
- type: Microwave
- type: Clickable
- type: InteractionOutline
- type: Solution
maxVol: 100
caps: 1
- type: Appearance
visuals:
- type: MicrowaveVisualizer
- type: Sound
- type: UserInterface
interfaces:
- key: enum.MicrowaveUiKey.Key
type: MicrowaveBoundUserInterface
- type: Collidable
shapes:
- !type:PhysShapeAabb
bounds: "-0.25,-0.4,0.25,0.4"
layer: 15
IsScrapingFloor: true
- type: Sprite
netsync: false
sprite: Objects/Kitchen/microwave.rsi
layers:
- state: mw0
map: ["enum.MicrowaveVisualizerLayers.Base"]
- state: mw_unlit
shader: unshaded
map: ["enum.MicrowaveVisualizerLayers.BaseUnlit"]
- type: PowerDevice
- type: Icon
sprite: Objects/Kitchen/microwave.rsi
state: mw0

View File

@@ -0,0 +1,338 @@
#Burgers
- type: microwaveMealRecipe
id: RecipeCheeseburger
name: Cheeseburger Recipe
result: FoodCheeseburger
time: 5
reagents:
chem.Flour: 5
solids:
FoodMeat: 1
- type: microwaveMealRecipe
id: RecipeTofuBurger
name: Tofu Burger Recipe
result: FoodTofuBurger
time: 5
reagents:
chem.Flour: 15
solids:
Tofu: 1
- type: microwaveMealRecipe
id: RecipeXenoburger
name: Xenoburger Recipe
result: FoodXenoburger
time: 5
reagents:
chem.Flour: 5
solids:
FoodXenomeat: 1
- type: microwaveMealRecipe
id: RecipeSuperBiteBurger
name: Super Bite Burger Recipe
result: FoodSuperBiteBurger
time: 20
reagents:
chem.Flour: 15
chem.Salt: 5
chem.Pepper: 5
solids:
FoodMeat: 5
FoodCheeseWedge: 3
FoodTomato: 4
FoodEgg: 2
#Breads & Sandwiches
- type: microwaveMealRecipe
id: RecipeBread
name: Bread Recipe
result: DrinkFoodContainerBread
time: 15
reagents:
chem.Flour: 15
- type: microwaveMealRecipe
id: RecipeSandwich
name: Sandwich Recipe
result: FoodSandwich
time: 10
solids:
FoodBreadSlice: 2
FoodMeatSteak: 1
FoodCheeseWedge: 1
- type: microwaveMealRecipe
id: RecipeToastedSandwich
name: Toasted Sandwich Recipe
result: FoodToastedSandwich
time: 5
solids:
FoodSandwich: 1
- type: microwaveMealRecipe
id: RecipeGrilledCheese
name: Grilled Cheese Recipe
result: FoodGrilledCheese
time: 5
solids:
FoodBreadSlice: 2
FoodCheeseWedge: 1
- type: microwaveMealRecipe
id: RecipeBaguette
name: Baguette Recipe
result: FoodBaguette
time: 10
reagents:
chem.Flour: 15
chem.Salt: 1
chem.Pepper: 1
- type: microwaveMealRecipe
id: RecipeCreamCheeseBread
name: Cream Cheese Bread Recipe
result: DrinkFoodContainerCreamCheeseBread
time: 20
reagents:
chem.Flour: 15
solids:
FoodCheeseWedge: 2
- type: microwaveMealRecipe
id: RecipeBananaBread
name: Banana Bread Recipe
result: DrinkFoodContainerBananaBread
time: 25
reagents:
chem.Flour: 15
chem.Milk: 5
solids:
FoodEgg: 3
FoodBanana: 1
#Pizzas
- type: microwaveMealRecipe
id: RecipeMargheritaPizza
name: Margherita Pizza Recipe
result: DrinkFoodContainerMargheritaPizza
time: 30
reagents:
chem.Flour: 10
solids:
FoodCheeseWedge: 4
FoodTomato: 1
- type: microwaveMealRecipe
id: RecipeMushroomPizza
name: Mushroom Pizza Recipe
result: DrinkFoodContainerMushroomPizza
time: 25
reagents:
chem.Flour: 10
solids:
FoodMushroom: 5
- type: microwaveMealRecipe
id: RecipeMeatPizza
name: Meat Pizza Recipe
result: DrinkFoodContainerMeatPizza
time: 30
reagents:
chem.Flour: 10
solids:
FoodMeat: 3
FoodCheeseWedge: 1
FoodTomato: 1
- type: microwaveMealRecipe
id: RecipeVegetablePizza
name: Vegetable Pizza Recipe
result: DrinkFoodContainerVegetablePizza
time: 30
reagents:
chem.Flour: 10
solids:
FoodEggplant: 1
FoodCarrot: 1
FoodCorn: 1
FoodTomato: 1
#Italian
- type: microwaveMealRecipe
id: RecipeSpaghetti
name: Spaghetti Recipe
result: FoodSpaghetti
time: 1
reagents:
chem.Flour: 5
- type: microwaveMealRecipe
id: RecipeBoiledSpaghetti
name: Boiled Spaghetti Recipe
result: FoodSpagettiBoiled
time: 5
reagents:
chem.H2O: 5
solids:
FoodSpagetti: 1
- type: microwaveMealRecipe
id: RecipePastaTomato
name: Pasta Tomato Recipe
result: FoodPastaTomato
time: 10
reagents:
chem.H2O: 5
solids:
FoodSpagetti: 1
FoodTomato: 2
- type: microwaveMealRecipe
id: RecipeMeatballSpaghetti
name: Spaghetti & Meatballs Recipe
result: FoodMeatballSpaghetti
time: 10
reagents:
chem.H2O: 5
solids:
FoodSpagetti: 1
FoodMeatball: 2
- type: microwaveMealRecipe
id: RecipeCrabSpaghetti
name: Crab Spaghetti Recipe
result: FoodCrabSpaghetti
time: 10
reagents:
chem.H2O: 5
solids:
FoodCrabMeat: 2
- type: microwaveMealRecipe
id: RecipeCopypasta
name: Copypasta Recipe
result: FoodCopypasta
time: 10
solids:
FoodPastaTomato: 2
# - type: microwaveMealRecipe
# id: RecipeMoMMISpaghetti
# name: MoMMI Spaghetti Recipe
# result: FoodMoMMISpaghetti
# time: 10
# reagents:
# chem.Flour: 5
# solids:
- type: microwaveMealRecipe
id: RecipeRisotto
name: Risotto Recipe
result: FoodRisotto
time: 10
reagents:
chem.Flour: 10 #This should be 10 units of rice.
chem.Wine: 5
solids:
FoodCheeseWedge: 1
- type: microwaveMealRecipe
id: RecipeBruschetta
name: Bruschetta Recipe
result: FoodBruschetta
time: 10
reagents:
chem.Flour: 10
chem.Salt: 2
solids:
FoodGarlic: 1
FoodCheeseWedge: 1
FoodTomato: 1
- type: microwaveMealRecipe
id: RecipeQuiche
name: Quiche Recipe
result: FoodQuiche
time: 10
solids:
FoodBlueTomato: 1
FoodGarlic: 1
FoodEgg: 1
FoodCheeseWedge: 1
- type: microwaveMealRecipe
id: RecipeLasagna
name: Lasagna Recipe
result: FoodLasagna
time: 15
# reagents:
# chem.TomatoJuice: 15
solids:
FoodFlatDough: 2
FoodMeat: 2
FoodEggPlant: 1
FoodCheeseWedge: 1
#Soups & Stew
- type: microwaveMealRecipe
id: RecipeMeatballSoup
name: Meatball Soup Recipe
result: FoodMeatballSoup
time: 10
reagents:
chem.H2O: 10
solids:
FoodMeatball: 1
FoodCarrot: 1
FoodPotato: 1
- type: microwaveMealRecipe
id: RecipeNettleSoup
name: Nettle Soup Recipe
result: FoodNettleSoup
time: 10
reagents:
chem.H2O: 10
solids:
FoodNettle: 1
FoodEgg: 1
FoodPotato: 1
#Other
- type: microwaveMealRecipe
id: RecipeMeatSteak
name: Meat Steak Recipe
result: FoodMeatSteak
time: 10
reagents:
chem.Salt: 1
chem.Pepper: 1
solids:
FoodMeat: 1
- type: microwaveMealRecipe
id: RecipeMisoColaSoup
name: Salty Sweet MiloCola Soup Recipe
result: DisgustingSweptSoup
time: 15
reagents:
chem.Cola: 5
solids:
FoodMiloSoup: 1
#Handy template for copy **pasta**. Get it? Pasta? Aw whatever fuck you.
# - type: microwaveMealRecipe
# id:
# name:
# result:
# time: 5
# reagents:
# solids:

View File

@@ -8,7 +8,9 @@
- type: latheRecipe - type: latheRecipe
id: Screwdriver id: Screwdriver
icon: Objects/Tools/screwdriver.png icon:
sprite: Objects/Tools/screwdriver.rsi
state: screwdriver
result: Screwdriver result: Screwdriver
completetime: 500 completetime: 500
materials: materials:
@@ -16,7 +18,9 @@
- type: latheRecipe - type: latheRecipe
id: Welder id: Welder
icon: Objects/Tools/autolathe_welder.png icon:
sprite: Objects/Tools/welder.rsi
state: welder
result: Welder result: Welder
completetime: 500 completetime: 500
materials: materials:
@@ -51,7 +55,9 @@
- type: latheRecipe - type: latheRecipe
id: Multitool id: Multitool
icon: Objects/Tools/multitool.png icon:
sprite: Objects/Tools/multitool.rsi
state: multitool
result: Multitool result: Multitool
completetime: 500 completetime: 500
materials: materials:

View File

@@ -0,0 +1,8 @@
- type: reagent
id: chem.Flour
name: Flour
desc: Used for baking.
color: "#FFFFFF"
metabolism:
- !type:DefaultFood
rate: 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,23 @@
{
"version":1,
"size":{
"x":96,
"y":96
},
"license":"CC-BY-SA-3.0",
"copyright":"Taken from https://github.com/tgstation/tgstation",
"states":[
{
"name":"on",
"directions": 1
},
{
"name": "off",
"directions": 1
},
{
"name": "broken",
"directions": 1
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,20 @@
{
"version":1,
"size":{
"x":32,
"y":32
},
"license":"CC-BY-SA-3.0",
"copyright":"Taken from https://github.com/tgstation/tgstation",
"states":[
{
"name": "activated",
"directions": 1,
"delays": [
[0.1,
0.1,
0.1]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,105 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "AGPL v3",
"copyright": "Taken from https://github.com/discordia-space/CEV-Eris",
"states": [
{
"name": "mw",
"directions": 1,
"delays": [
[
1.0
]
]
},
{
"name": "mw_unlit",
"directions": 1,
"delays": [
[
1.0
]
]
},
{
"name": "mw0",
"directions": 1,
"delays": [
[
1.0
]
]
},
{
"name": "mw_running_unlit",
"directions": 1,
"delays": [
[
1.0,
1.0
]
]
},
{
"name": "mwb",
"directions": 1,
"delays": [
[
1.0
]
]
},
{
"name": "mwbloody",
"directions": 1,
"delays": [
[
1.0
]
]
},
{
"name": "mwbloody0",
"directions": 1,
"delays": [
[
1.0
]
]
},
{
"name": "mwbloody1",
"directions": 1,
"delays": [
[
0.1,
0.1
]
]
},
{
"name": "mwbloodyo",
"directions": 1,
"delays": [
[
0.1,
0.1
]
]
},
{
"name": "mwo",
"directions": 1,
"delays": [
[
0.1,
0.1
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

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