Merge branch 'master' into kitchen
2
.vscode/extensions.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-vscode.csharp",
|
||||
"ms-dotnettools.csharp",
|
||||
"editorconfig.editorconfig"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -211,19 +211,19 @@ namespace Content.Client.Chat
|
||||
case OOCAlias:
|
||||
{
|
||||
var conInput = text.Substring(1);
|
||||
_console.ProcessCommand($"ooc \"{conInput}\"");
|
||||
_console.ProcessCommand($"ooc \"{CommandParsing.Escape(conInput)}\"");
|
||||
break;
|
||||
}
|
||||
case MeAlias:
|
||||
{
|
||||
var conInput = text.Substring(1);
|
||||
_console.ProcessCommand($"me \"{conInput}\"");
|
||||
_console.ProcessCommand($"me \"{CommandParsing.Escape(conInput)}\"");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
var conInput = _currentChatBox.DefaultChatFormat != null
|
||||
? string.Format(_currentChatBox.DefaultChatFormat, text)
|
||||
? string.Format(_currentChatBox.DefaultChatFormat, CommandParsing.Escape(text))
|
||||
: text;
|
||||
_console.ProcessCommand(conInput);
|
||||
break;
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Content.Client
|
||||
IoCManager.Register<IClientPreferencesManager, ClientPreferencesManager>();
|
||||
IoCManager.Register<IItemSlotManager, ItemSlotManager>();
|
||||
IoCManager.Register<IStylesheetManager, StylesheetManager>();
|
||||
IoCManager.Register<IScreenshotHook, ScreenshotHook>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.Interfaces;
|
||||
using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared;
|
||||
using Robust.Client.Interfaces.Console;
|
||||
using Robust.Client.Interfaces.Graphics.ClientEye;
|
||||
@@ -57,7 +58,11 @@ namespace Content.Client
|
||||
|
||||
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;
|
||||
LayoutContainer.SetPosition(label, label.InitialPos = coordinates.Position - minimumSize / 2);
|
||||
_userInterfaceManager.PopupRoot.AddChild(label);
|
||||
|
||||
@@ -12,6 +12,7 @@ using Content.Client.UserInterface.Stylesheets;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Cargo;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Gravity;
|
||||
using Content.Shared.GameObjects.Components.Markers;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.GameObjects.Components.VendingMachines;
|
||||
@@ -162,6 +163,8 @@ namespace Content.Client
|
||||
factory.Register<SharedCargoConsoleComponent>();
|
||||
factory.Register<SharedReagentDispenserComponent>();
|
||||
factory.Register<SharedMicrowaveComponent>();
|
||||
factory.Register<SharedGravityGeneratorComponent>();
|
||||
|
||||
prototypes.RegisterIgnore("material");
|
||||
prototypes.RegisterIgnore("reaction"); //Chemical reactions only needed by server. Reactions checks are server-side.
|
||||
prototypes.RegisterIgnore("barSign");
|
||||
@@ -179,6 +182,7 @@ namespace Content.Client
|
||||
IoCManager.Resolve<IParallaxManager>().LoadParallax();
|
||||
IoCManager.Resolve<IBaseClient>().PlayerJoinedServer += SubscribePlayerAttachmentEvents;
|
||||
IoCManager.Resolve<IStylesheetManager>().Initialize();
|
||||
IoCManager.Resolve<IScreenshotHook>().Initialize();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,6 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
[UsedImplicitly]
|
||||
public sealed class CombatModeSystem : SharedCombatModeSystem
|
||||
{
|
||||
private const float AttackTimeThreshold = 0.15f;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IGameHud _gameHud;
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
@@ -37,9 +35,6 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
|
||||
private InputSystem _inputSystem;
|
||||
|
||||
public bool UseOrAttackIsDown { get; private set; }
|
||||
private float _timeHeld;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -48,10 +43,8 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
_gameHud.OnTargetingZoneChanged = OnTargetingZoneChanged;
|
||||
|
||||
_inputSystem = EntitySystemManager.GetEntitySystem<InputSystem>();
|
||||
_inputSystem.BindMap.BindFunction(ContentKeyFunctions.UseOrAttack, new InputHandler(this));
|
||||
_inputSystem.BindMap.BindFunction(ContentKeyFunctions.ToggleCombatMode,
|
||||
InputCmdHandler.FromDelegate(CombatModeToggled));
|
||||
_overlayManager.AddOverlay(new CombatModeOverlay(this));
|
||||
}
|
||||
|
||||
private void CombatModeToggled(ICommonSession session)
|
||||
@@ -60,20 +53,10 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
{
|
||||
EntityManager.RaisePredictiveEvent(
|
||||
new CombatModeSystemMessages.SetCombatModeActiveMessage(!IsInCombatMode()));
|
||||
|
||||
// Just in case.
|
||||
UseOrAttackIsDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_overlayManager.RemoveOverlay(nameof(CombatModeOverlay));
|
||||
}
|
||||
|
||||
private bool IsInCombatMode()
|
||||
public bool IsInCombatMode()
|
||||
{
|
||||
var entity = _playerManager.LocalPlayer.ControlledEntity;
|
||||
if (entity == null || !entity.TryGetComponent(out CombatModeComponent combatMode))
|
||||
@@ -92,104 +75,6 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
private void OnCombatModeChanged(bool 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
base.Update(frameTime);
|
||||
|
||||
var canFireSemi = _isFirstShot;
|
||||
var state = _inputSystem.CmdStates.GetState(ContentKeyFunctions.Attack);
|
||||
if (!_combatModeSystem.UseOrAttackIsDown && state != BoundKeyState.Down)
|
||||
var state = _inputSystem.CmdStates.GetState(EngineKeyFunctions.Use);
|
||||
if (!_combatModeSystem.IsInCombatMode() && state != BoundKeyState.Down)
|
||||
{
|
||||
_isFirstShot = true;
|
||||
_blocked = false;
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace Content.Client.Input
|
||||
common.AddFunction(ContentKeyFunctions.FocusChat);
|
||||
common.AddFunction(ContentKeyFunctions.ExamineEntity);
|
||||
common.AddFunction(ContentKeyFunctions.OpenTutorial);
|
||||
common.AddFunction(ContentKeyFunctions.UseOrAttack);
|
||||
common.AddFunction(ContentKeyFunctions.TakeScreenshot);
|
||||
common.AddFunction(ContentKeyFunctions.TakeScreenshotNoUI);
|
||||
|
||||
var human = contexts.GetContext("human");
|
||||
human.AddFunction(ContentKeyFunctions.SwapHands);
|
||||
@@ -27,9 +28,11 @@ namespace Content.Client.Input
|
||||
human.AddFunction(ContentKeyFunctions.OpenContextMenu);
|
||||
human.AddFunction(ContentKeyFunctions.OpenCraftingMenu);
|
||||
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
|
||||
human.AddFunction(ContentKeyFunctions.SmartEquipBackpack);
|
||||
human.AddFunction(ContentKeyFunctions.SmartEquipBelt);
|
||||
human.AddFunction(ContentKeyFunctions.MouseMiddle);
|
||||
human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
|
||||
human.AddFunction(ContentKeyFunctions.Attack);
|
||||
human.AddFunction(ContentKeyFunctions.WideAttack);
|
||||
|
||||
var ghost = contexts.New("ghost", "common");
|
||||
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
||||
|
||||
85
Content.Client/ScreenshotHook.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ namespace Content.Client.UserInterface.Stylesheets
|
||||
public const string StyleClassLabelSecondaryColor = "LabelSecondaryColor";
|
||||
public const string StyleClassLabelBig = "LabelBig";
|
||||
public const string StyleClassButtonBig = "ButtonBig";
|
||||
public const string StyleClassPopupMessage = "PopupMessage";
|
||||
|
||||
public static readonly Color NanoGold = Color.FromHex("#A88B5E");
|
||||
|
||||
@@ -41,6 +42,7 @@ namespace Content.Client.UserInterface.Stylesheets
|
||||
public StyleNano(IResourceCache resCache) : base(resCache)
|
||||
{
|
||||
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 notoSansBold12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Bold.ttf", 12);
|
||||
var notoSansDisplayBold14 = resCache.GetFont("/Fonts/NotoSansDisplay/NotoSansDisplay-Bold.ttf", 14);
|
||||
@@ -552,6 +554,14 @@ namespace Content.Client.UserInterface.Stylesheets
|
||||
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
|
||||
new StyleRule(new SelectorElement(typeof(Label), new[] {StyleClassPowerStateNone}, null, null), new[]
|
||||
{
|
||||
|
||||
@@ -69,10 +69,14 @@ namespace Content.Client.UserInterface
|
||||
Switch hands: [color=#a4885c]{4}[/color]
|
||||
Use held item: [color=#a4885c]{5}[/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 character window: [color=#a4885c]{8}[/color]
|
||||
Open crafting window: [color=#a4885c]{9}[/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]
|
||||
Throw held item: [color=#a4885c]{12}[/color]
|
||||
Examine entity: [color=#a4885c]{13}[/color]
|
||||
@@ -102,16 +106,20 @@ Toggle sandbox window: [color=#a4885c]{21}[/color]",
|
||||
Key(ShowDebugMonitors),
|
||||
Key(OpenEntitySpawnWindow),
|
||||
Key(OpenTileSpawnWindow),
|
||||
Key(OpenSandboxWindow)));
|
||||
|
||||
//Gameplay
|
||||
VBox.AddChild(new Label { FontOverride = headerFont, Text = Loc.GetString("\nSandbox spawner", Key(OpenSandboxWindow)) });
|
||||
AddFormattedText(SandboxSpawnerContents);
|
||||
Key(OpenSandboxWindow),
|
||||
Key(Use),
|
||||
Key(WideAttack),
|
||||
Key(SmartEquipBackpack),
|
||||
Key(SmartEquipBelt)));
|
||||
|
||||
//Gameplay
|
||||
VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nGameplay" });
|
||||
AddFormattedText(GameplayContents);
|
||||
|
||||
//Gameplay
|
||||
VBox.AddChild(new Label { FontOverride = headerFont, Text = Loc.GetString("\nSandbox spawner", Key(OpenSandboxWindow)) });
|
||||
AddFormattedText(SandboxSpawnerContents);
|
||||
|
||||
//Feedback
|
||||
VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nFeedback" });
|
||||
AddFormattedText(FeedbackContents);
|
||||
|
||||
64
Content.IntegrationTests/Tests/GravityGridTest.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,6 +151,12 @@ namespace Content.Server.GameObjects
|
||||
return success;
|
||||
}
|
||||
|
||||
public void PutInHandOrDrop(ItemComponent item)
|
||||
{
|
||||
if (!PutInHand(item))
|
||||
item.Owner.Transform.GridPosition = Owner.Transform.GridPosition;
|
||||
}
|
||||
|
||||
public bool CanPutInHand(ItemComponent item)
|
||||
{
|
||||
foreach (var hand in ActivePriorityEnumerable())
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,8 @@ namespace Content.Server.GameObjects
|
||||
private int StorageCapacityMax = 10000;
|
||||
public HashSet<IPlayerSession> SubscribedSessions = new HashSet<IPlayerSession>();
|
||||
|
||||
public IReadOnlyCollection<IEntity> StoredEntities => storage.ContainedEntities;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -140,7 +142,6 @@ namespace Content.Server.GameObjects
|
||||
/// <returns></returns>
|
||||
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);
|
||||
|
||||
if(Owner.TryGetComponent<PlaceableSurfaceComponent>(out var placeableSurfaceComponent))
|
||||
@@ -363,8 +364,10 @@ namespace Content.Server.GameObjects
|
||||
/// <summary>
|
||||
/// Inserts an entity into the storage component from the players active hand.
|
||||
/// </summary>
|
||||
private bool PlayerInsertEntity(IEntity player)
|
||||
public bool PlayerInsertEntity(IEntity player)
|
||||
{
|
||||
_ensureInitialCalculated();
|
||||
|
||||
if (!player.TryGetComponent(out IHandsComponent hands) || hands.GetActiveHand == null)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -118,9 +118,17 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
if (!ShowExamineInfo)
|
||||
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
|
||||
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)
|
||||
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]");
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace Content.Server.GameObjects
|
||||
|
||||
currentstate = threshold;
|
||||
|
||||
EntityEventArgs toRaise = new MobDamageStateChangedMessage(this);
|
||||
var toRaise = new MobDamageStateChangedMessage(this);
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, toRaise);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
/// Is the entity Sprinting (running)?
|
||||
/// </summary>
|
||||
|
||||
@@ -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>
|
||||
/// Is the entity Sprinting (running)?
|
||||
/// </summary>
|
||||
|
||||
@@ -34,6 +34,15 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float CurrentWalkSpeed { get; set; } = 8;
|
||||
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 Vector2 VelocityDir { get; } = Vector2.Zero;
|
||||
public GridCoordinates LastPosition { get; set; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Audio;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -16,17 +17,24 @@ namespace Content.Server.GameObjects.Components.Sound
|
||||
public override string Name => "EmitSoundOnUse";
|
||||
|
||||
public string _soundName;
|
||||
public float _pitchVariation;
|
||||
|
||||
public override void ExposeData(ObjectSerializer 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)
|
||||
{
|
||||
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));
|
||||
return true;
|
||||
}
|
||||
@@ -302,8 +302,8 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
|
||||
inputSys.BindMap.BindFunction(EngineKeyFunctions.Use,
|
||||
new PointerInputCmdHandler(HandleUseItemInHand));
|
||||
inputSys.BindMap.BindFunction(ContentKeyFunctions.Attack,
|
||||
new PointerInputCmdHandler(HandleAttack));
|
||||
inputSys.BindMap.BindFunction(ContentKeyFunctions.WideAttack,
|
||||
new PointerInputCmdHandler(HandleWideAttack));
|
||||
inputSys.BindMap.BindFunction(ContentKeyFunctions.ActivateItemInWorld,
|
||||
new PointerInputCmdHandler(HandleActivateItemInWorld));
|
||||
}
|
||||
@@ -362,7 +362,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
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
|
||||
if (!_mapManager.GridExists(coords.GridID))
|
||||
|
||||
134
Content.Server/GameObjects/EntitySystems/GravitySystem.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects;
|
||||
using Content.Server.GameObjects.Components;
|
||||
using Content.Server.GameObjects.Components.Stack;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Server.Throw;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Physics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
@@ -19,6 +24,7 @@ using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Physics;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -33,6 +39,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
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.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem));
|
||||
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 />
|
||||
@@ -126,7 +135,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
|
||||
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))
|
||||
{
|
||||
handsComp.Drop(handsComp.ActiveIndex, coords);
|
||||
@@ -190,5 +199,53 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
|
||||
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>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.Movement;
|
||||
using Content.Server.GameObjects.Components.Sound;
|
||||
@@ -15,6 +17,7 @@ using Robust.Server.Interfaces.Player;
|
||||
using Robust.Server.Interfaces.Timing;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Input;
|
||||
@@ -28,6 +31,7 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
@@ -44,6 +48,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager;
|
||||
[Dependency] private readonly IEntityManager _entityManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private AudioSystem _audioSystem;
|
||||
@@ -130,13 +135,43 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
}
|
||||
var mover = entity.GetComponent<IMoverComponent>();
|
||||
var physics = entity.GetComponent<PhysicsComponent>();
|
||||
|
||||
UpdateKinematics(entity.Transform, mover, physics);
|
||||
if (entity.TryGetComponent<CollidableComponent>(out var collider))
|
||||
{
|
||||
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 (physics.LinearVelocity != Vector2.Zero)
|
||||
@@ -145,6 +180,13 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
}
|
||||
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);
|
||||
transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle();
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
namespace Content.Server.GameTicking
|
||||
using System.Collections.Generic;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
|
||||
namespace Content.Server.GameTicking
|
||||
{
|
||||
/// <summary>
|
||||
/// A round-start setup preset, such as which antagonists to spawn.
|
||||
/// </summary>
|
||||
public abstract class GamePreset
|
||||
{
|
||||
public abstract void Start();
|
||||
public abstract bool Start(IReadOnlyList<IPlayerSession> players);
|
||||
public virtual string ModeTitle => "Sandbox";
|
||||
public virtual string Description => "Secret!";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Content.Server.GameTicking.GameRules;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameTicking.GameRules;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.GameTicking.GamePresets
|
||||
@@ -10,9 +12,10 @@ namespace Content.Server.GameTicking.GamePresets
|
||||
[Dependency] private readonly IGameTicker _gameTicker;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void Start()
|
||||
public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers)
|
||||
{
|
||||
_gameTicker.AddGameRule<RuleDeathMatch>();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ModeTitle => "Deathmatch";
|
||||
|
||||
@@ -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;
|
||||
|
||||
namespace Content.Server.GameTicking.GamePresets
|
||||
@@ -9,9 +11,10 @@ namespace Content.Server.GameTicking.GamePresets
|
||||
[Dependency] private readonly ISandboxManager _sandboxManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void Start()
|
||||
public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers)
|
||||
{
|
||||
_sandboxManager.IsSandboxEnabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ModeTitle => "Sandbox";
|
||||
|
||||
63
Content.Server/GameTicking/GamePresets/PresetSuspicion.cs
Normal 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?";
|
||||
}
|
||||
}
|
||||
122
Content.Server/GameTicking/GameRules/RuleSuspicion.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,8 @@ namespace Content.Server.GameTicking
|
||||
|
||||
_configurationManager.RegisterCVar("game.lobbyenabled", false, 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;
|
||||
|
||||
@@ -181,11 +182,6 @@ namespace Content.Server.GameTicking
|
||||
|
||||
SendServerMessage("The round is starting now...");
|
||||
|
||||
RunLevel = GameRunLevel.InRound;
|
||||
|
||||
var preset = MakeGamePreset();
|
||||
preset.Start();
|
||||
|
||||
List<IPlayerSession> readyPlayers;
|
||||
if (LobbyEnabled)
|
||||
{
|
||||
@@ -196,6 +192,8 @@ namespace Content.Server.GameTicking
|
||||
readyPlayers = _playersInLobby.Keys.ToList();
|
||||
}
|
||||
|
||||
RunLevel = GameRunLevel.InRound;
|
||||
|
||||
// Get the profiles for each player for easier lookup.
|
||||
var profiles = readyPlayers.ToDictionary(p => p, GetPlayerProfile);
|
||||
|
||||
@@ -222,6 +220,18 @@ namespace Content.Server.GameTicking
|
||||
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;
|
||||
_sendStatusToAll();
|
||||
}
|
||||
@@ -255,15 +265,16 @@ namespace Content.Server.GameTicking
|
||||
var listOfPlayerInfo = new List<RoundEndPlayerInfo>();
|
||||
foreach(var ply in _playerManager.GetAllPlayers().OrderBy(p => p.Name))
|
||||
{
|
||||
if(ply.AttachedEntity.TryGetComponent<MindComponent>(out var mindComponent)
|
||||
&& mindComponent.HasMind)
|
||||
var mind = ply.ContentData().Mind;
|
||||
if(mind != null)
|
||||
{
|
||||
var antag = mind.AllRoles.Any(role => role.Antag);
|
||||
var playerEndRoundInfo = new RoundEndPlayerInfo()
|
||||
{
|
||||
PlayerOOCName = ply.Name,
|
||||
PlayerICName = mindComponent.Mind.CurrentEntity.Name,
|
||||
Role = mindComponent.Mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("Unkown"),
|
||||
Antag = false
|
||||
PlayerICName = mind.CurrentEntity.Name,
|
||||
Role = antag ? mind.AllRoles.First(role => role.Antag).Name : mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("Unkown"),
|
||||
Antag = antag
|
||||
};
|
||||
listOfPlayerInfo.Add(playerEndRoundInfo);
|
||||
}
|
||||
@@ -339,6 +350,7 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
"Sandbox" => typeof(PresetSandbox),
|
||||
"DeathMatch" => typeof(PresetDeathMatch),
|
||||
"Suspicion" => typeof(PresetSuspicion),
|
||||
_ => throw new NotSupportedException()
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,17 @@ namespace Content.Server.Interfaces.GameObjects.Components.Movement
|
||||
/// </summary>
|
||||
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>
|
||||
/// Is the entity Sprinting (running)?
|
||||
/// </summary>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Players;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Mobs
|
||||
@@ -130,6 +133,13 @@ namespace Content.Server.Mobs
|
||||
_roles.Remove(role);
|
||||
}
|
||||
|
||||
public bool HasRole<T>() where T : Role
|
||||
{
|
||||
var t = typeof(T);
|
||||
|
||||
return _roles.Any(role => role.GetType() == t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transfer this mind's control over to a new entity.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Hey look,
|
||||
// Antag Datums.
|
||||
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Mobs
|
||||
{
|
||||
/// <summary>
|
||||
@@ -20,6 +23,11 @@ namespace Content.Server.Mobs
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
Mind = mind;
|
||||
|
||||
@@ -11,8 +11,9 @@ namespace Content.Server.Mobs.Roles
|
||||
public JobPrototype Prototype { 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)
|
||||
{
|
||||
@@ -25,9 +26,7 @@ namespace Content.Server.Mobs.Roles
|
||||
base.Greet();
|
||||
|
||||
var chat = IoCManager.Resolve<IChatManager>();
|
||||
chat.DispatchServerMessage(
|
||||
Mind.Session,
|
||||
String.Format("You're a new {0}. Do your best!", Name));
|
||||
chat.DispatchServerMessage(Mind.Session, $"You're a new {Name}. Do your best!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
25
Content.Server/Mobs/Roles/SuspicionInnocentRole.cs
Normal 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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Content.Server/Mobs/Roles/SuspicionTraitorRole.cs
Normal 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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
Content.Shared/Audio/AudioHelpers.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared
|
||||
{
|
||||
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
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void PreInit()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
// Default to en-US.
|
||||
_localizationManager.LoadCulture(new CultureInfo(Culture));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public override void PostInit()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,5 +43,6 @@
|
||||
public const uint REAGENT_INJECTOR = 1038;
|
||||
public const uint GHOST = 1039;
|
||||
public const uint MICROWAVE = 1040;
|
||||
public const uint GRAVITY_GENERATOR = 1041;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@ namespace Content.Shared.Input
|
||||
[KeyFunctions]
|
||||
public static class ContentKeyFunctions
|
||||
{
|
||||
public static readonly BoundKeyFunction UseOrAttack = "UseOrAttack";
|
||||
public static readonly BoundKeyFunction Attack = "Attack";
|
||||
public static readonly BoundKeyFunction WideAttack = "WideAttack";
|
||||
public static readonly BoundKeyFunction ActivateItemInHand = "ActivateItemInHand";
|
||||
public static readonly BoundKeyFunction ActivateItemInWorld = "ActivateItemInWorld"; // default action on world entity
|
||||
public static readonly BoundKeyFunction Drop = "Drop";
|
||||
@@ -16,6 +15,8 @@ namespace Content.Shared.Input
|
||||
public static readonly BoundKeyFunction OpenContextMenu = "OpenContextMenu";
|
||||
public static readonly BoundKeyFunction OpenCraftingMenu = "OpenCraftingMenu";
|
||||
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 SwapHands = "SwapHands";
|
||||
public static readonly BoundKeyFunction ThrowItemInHand = "ThrowItemInHand";
|
||||
@@ -24,5 +25,7 @@ namespace Content.Shared.Input
|
||||
public static readonly BoundKeyFunction OpenEntitySpawnWindow = "OpenEntitySpawnWindow";
|
||||
public static readonly BoundKeyFunction OpenSandboxWindow = "OpenSandboxWindow";
|
||||
public static readonly BoundKeyFunction OpenTileSpawnWindow = "OpenTileSpawnWindow";
|
||||
public static readonly BoundKeyFunction TakeScreenshot = "TakeScreenshot";
|
||||
public static readonly BoundKeyFunction TakeScreenshotNoUI = "TakeScreenshotNoUI";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace Content.Shared
|
||||
|
||||
public string GamemodeTitle;
|
||||
public TimeSpan RoundDuration;
|
||||
|
||||
|
||||
|
||||
public uint PlayerCount;
|
||||
|
||||
|
||||
2
Jenkinsfile
vendored
@@ -9,7 +9,7 @@ pipeline {
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'Tools/package_release_build.py -p windows mac linux'
|
||||
sh 'Tools/package_release_build.py -p windows mac linux linux-arm64'
|
||||
}
|
||||
}
|
||||
stage('Update build info') {
|
||||
|
||||
BIN
Resources/Audio/effects/alert.ogg
Normal file
@@ -134,3 +134,4 @@
|
||||
- gc_mode
|
||||
CanViewVar: true
|
||||
CanAdminPlace: true
|
||||
CanScript: true
|
||||
|
||||
19
Resources/Locale/nl-NL/tools.yml
Normal 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.
|
||||
@@ -117,3 +117,26 @@
|
||||
|
||||
- type: Icon
|
||||
sprite: Buildings/Doors/airlock_command_glass.rsi
|
||||
|
||||
|
||||
- type: entity
|
||||
parent: Airlock
|
||||
id: AirlockSecurity
|
||||
name: Security Airlock
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Buildings/Doors/airlock_security.rsi
|
||||
|
||||
- type: Icon
|
||||
sprite: Buildings/Doors/airlock_security.rsi
|
||||
|
||||
- type: entity
|
||||
parent: AirlockGlass
|
||||
id: AirlockSecurityGlass
|
||||
name: Glass Security Airlock
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Buildings/Doors/airlock_security_glass.rsi
|
||||
|
||||
- type: Icon
|
||||
sprite: Buildings/Doors/airlock_security_glass.rsi
|
||||
|
||||
@@ -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
|
||||
@@ -36,7 +36,6 @@
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: MedicalScannerVisualizer2D
|
||||
- type: PowerDevice
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.MedicalScannerUiKey.Key
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
state: utilitybelt
|
||||
- type: Clothing
|
||||
Size: 50
|
||||
QuickEquip: false
|
||||
sprite: Clothing/belt_utility.rsi
|
||||
- type: Storage
|
||||
Capacity: 40 # Full tool loadout is 35, plus an extra
|
||||
|
||||
@@ -23,6 +23,18 @@
|
||||
- type: Icon
|
||||
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
|
||||
name: Violin
|
||||
parent: BaseHandheldInstrument
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
- type: Sound
|
||||
- type: EmitSoundOnUse
|
||||
sound: /Audio/items/bikehorn.ogg
|
||||
variation: 0.2
|
||||
|
||||
- type: UseDelay
|
||||
delay: 0.5
|
||||
|
||||
@@ -42,6 +42,3 @@
|
||||
- type: Item
|
||||
Size: 24
|
||||
sprite: Objects/Guns/SMGs/c20r.rsi
|
||||
- type: Item
|
||||
Size: 24
|
||||
sprite: Objects/Guns/SMGs/wt550.rsi
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
drawdepth: Objects
|
||||
- type: Icon
|
||||
texture: Objects/Janitorial/mopbucket.png
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Bucket
|
||||
- type: Sound
|
||||
@@ -51,7 +50,6 @@
|
||||
- type: Physics
|
||||
mass: 5
|
||||
Anchored: false
|
||||
- type: Sound
|
||||
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
- type: latheRecipe
|
||||
id: Screwdriver
|
||||
icon: Objects/Tools/screwdriver.png
|
||||
icon:
|
||||
sprite: Objects/Tools/screwdriver.rsi
|
||||
state: screwdriver
|
||||
result: Screwdriver
|
||||
completetime: 500
|
||||
materials:
|
||||
@@ -16,7 +18,9 @@
|
||||
|
||||
- type: latheRecipe
|
||||
id: Welder
|
||||
icon: Objects/Tools/autolathe_welder.png
|
||||
icon:
|
||||
sprite: Objects/Tools/welder.rsi
|
||||
state: welder
|
||||
result: Welder
|
||||
completetime: 500
|
||||
materials:
|
||||
@@ -51,7 +55,9 @@
|
||||
|
||||
- type: latheRecipe
|
||||
id: Multitool
|
||||
icon: Objects/Tools/multitool.png
|
||||
icon:
|
||||
sprite: Objects/Tools/multitool.rsi
|
||||
state: multitool
|
||||
result: Multitool
|
||||
completetime: 500
|
||||
materials:
|
||||
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 165 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 316 B |
BIN
Resources/Textures/Buildings/Doors/airlock_security.rsi/deny.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,220 @@
|
||||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from https://github.com/discordia-space/CEV-Eris/blob/2b969adc2dfd3e9621bf3597c5cbffeb3ac8c9f0/icons/obj/doors/Dooreng.dmi",
|
||||
"states": [
|
||||
{
|
||||
"name": "closed",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "closed_unlit",
|
||||
"directions": 1
|
||||
},
|
||||
{
|
||||
"name": "closing",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "closing_unlit",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.5,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "deny",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "locked",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "panel_closing",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "panel_opening",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "open",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "opening",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "opening_unlit",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "panel_open",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spark",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sparks_broken",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sparks_damaged",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
1.7
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sparks_open",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "welded",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Buildings/Doors/airlock_security.rsi/open.png
Normal file
|
After Width: | Height: | Size: 291 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 368 B |
|
After Width: | Height: | Size: 193 B |
|
After Width: | Height: | Size: 348 B |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 226 B |
|
After Width: | Height: | Size: 238 B |
|
After Width: | Height: | Size: 215 B |
|
After Width: | Height: | Size: 225 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 165 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 316 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,220 @@
|
||||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from https://github.com/discordia-space/CEV-Eris/blob/2b969adc2dfd3e9621bf3597c5cbffeb3ac8c9f0/icons/obj/doors/Dooreng.dmi",
|
||||
"states": [
|
||||
{
|
||||
"name": "closed",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "closed_unlit",
|
||||
"directions": 1
|
||||
},
|
||||
{
|
||||
"name": "closing",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "closing_unlit",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.5,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "deny",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "locked",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "panel_closing",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "panel_opening",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "open",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "opening",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "opening_unlit",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.5
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "panel_open",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spark",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sparks_broken",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sparks_damaged",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
1.7
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "sparks_open",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "welded",
|
||||
"directions": 1,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 291 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 368 B |
|
After Width: | Height: | Size: 193 B |
|
After Width: | Height: | Size: 348 B |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 226 B |
|
After Width: | Height: | Size: 238 B |
|
After Width: | Height: | Size: 215 B |
|
After Width: | Height: | Size: 225 B |
BIN
Resources/Textures/Buildings/gravity_generator.rsi/broken.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
23
Resources/Textures/Buildings/gravity_generator.rsi/meta.json
Executable 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
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Buildings/gravity_generator.rsi/off.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
Resources/Textures/Buildings/gravity_generator.rsi/on.png
Executable file
|
After Width: | Height: | Size: 18 KiB |