diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 945465607f..83bca6f97b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,6 @@ { "recommendations": [ - "ms-vscode.csharp", + "ms-dotnettools.csharp", "editorconfig.editorconfig" ] } diff --git a/Content.Client/Chat/ChatManager.cs b/Content.Client/Chat/ChatManager.cs index 8a0fcdeabd..8592028d11 100644 --- a/Content.Client/Chat/ChatManager.cs +++ b/Content.Client/Chat/ChatManager.cs @@ -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; diff --git a/Content.Client/ClientContentIoC.cs b/Content.Client/ClientContentIoC.cs index 690de5c18a..93e75a88f1 100644 --- a/Content.Client/ClientContentIoC.cs +++ b/Content.Client/ClientContentIoC.cs @@ -31,6 +31,7 @@ namespace Content.Client IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); } } } diff --git a/Content.Client/ClientNotifyManager.cs b/Content.Client/ClientNotifyManager.cs index 1370dcca16..867bee8144 100644 --- a/Content.Client/ClientNotifyManager.cs +++ b/Content.Client/ClientNotifyManager.cs @@ -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); diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 971096182b..7604f3e8e9 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -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(); factory.Register(); factory.Register(); + factory.Register(); + 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().LoadParallax(); IoCManager.Resolve().PlayerJoinedServer += SubscribePlayerAttachmentEvents; IoCManager.Resolve().Initialize(); + IoCManager.Resolve().Initialize(); IoCManager.InjectDependencies(this); diff --git a/Content.Client/GameObjects/Components/Gravity/GravityGeneratorBoundUserInterface.cs b/Content.Client/GameObjects/Components/Gravity/GravityGeneratorBoundUserInterface.cs new file mode 100644 index 0000000000..450e73c288 --- /dev/null +++ b/Content.Client/GameObjects/Components/Gravity/GravityGeneratorBoundUserInterface.cs @@ -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"); + } + } +} diff --git a/Content.Client/GameObjects/EntitySystems/CombatModeSystem.cs b/Content.Client/GameObjects/EntitySystems/CombatModeSystem.cs index bdc5bc0a44..60ecdcd3fe 100644 --- a/Content.Client/GameObjects/EntitySystems/CombatModeSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/CombatModeSystem.cs @@ -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.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().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)); - } - } } } } diff --git a/Content.Client/GameObjects/EntitySystems/RangedWeaponSystem.cs b/Content.Client/GameObjects/EntitySystems/RangedWeaponSystem.cs index d34001cb27..8a0c991055 100644 --- a/Content.Client/GameObjects/EntitySystems/RangedWeaponSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/RangedWeaponSystem.cs @@ -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; diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs index 26e850a3a3..279b238217 100644 --- a/Content.Client/Input/ContentContexts.cs +++ b/Content.Client/Input/ContentContexts.cs @@ -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); diff --git a/Content.Client/ScreenshotHook.cs b/Content.Client/ScreenshotHook.cs new file mode 100644 index 0000000000..529a469b72 --- /dev/null +++ b/Content.Client/ScreenshotHook.cs @@ -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(); + } +} diff --git a/Content.Client/UserInterface/Stylesheets/StyleNano.cs b/Content.Client/UserInterface/Stylesheets/StyleNano.cs index e182012a55..a96413b9d0 100644 --- a/Content.Client/UserInterface/Stylesheets/StyleNano.cs +++ b/Content.Client/UserInterface/Stylesheets/StyleNano.cs @@ -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[] { diff --git a/Content.Client/UserInterface/TutorialWindow.cs b/Content.Client/UserInterface/TutorialWindow.cs index cc81193dbf..af54907d57 100644 --- a/Content.Client/UserInterface/TutorialWindow.cs +++ b/Content.Client/UserInterface/TutorialWindow.cs @@ -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); diff --git a/Content.IntegrationTests/Tests/GravityGridTest.cs b/Content.IntegrationTests/Tests/GravityGridTest.cs new file mode 100644 index 0000000000..0b35fa603a --- /dev/null +++ b/Content.IntegrationTests/Tests/GravityGridTest.cs @@ -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(); + + mapMan.CreateMap(new MapId(1)); + grid1 = mapMan.CreateGrid(new MapId(1)); + grid2 = mapMan.CreateGrid(new MapId(1)); + + var entityMan = IoCManager.Resolve(); + + generator = entityMan.SpawnEntity("GravityGenerator", new GridCoordinates(new Vector2(0, 0), grid2.Index)); + Assert.That(generator.HasComponent()); + Assert.That(generator.HasComponent()); + var generatorComponent = generator.GetComponent(); + var powerComponent = generator.GetComponent(); + Assert.AreEqual(generatorComponent.Status, GravityGeneratorStatus.Unpowered); + powerComponent.ExternalPowered = true; + }); + server.RunTicks(1); + + server.Assert(() => + { + var generatorComponent = generator.GetComponent(); + + Assert.AreEqual(generatorComponent.Status, GravityGeneratorStatus.On); + + Assert.That(!grid1.HasGravity); + Assert.That(grid2.HasGravity); + }); + + await server.WaitIdleAsync(); + } + } +} diff --git a/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs b/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs index 5375c3b783..ab265e3693 100644 --- a/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/ServerHandsComponent.cs @@ -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()) diff --git a/Content.Server/GameObjects/Components/Gravity/GravityGeneratorComponent.cs b/Content.Server/GameObjects/Components/Gravity/GravityGeneratorComponent.cs new file mode 100644 index 0000000000..494a97c9d8 --- /dev/null +++ b/Content.Server/GameObjects/Components/Gravity/GravityGeneratorComponent.cs @@ -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() + .GetBoundUserInterface(GravityGeneratorUiKey.Key); + _userInterface.OnReceiveMessage += HandleUIMessage; + _powerDevice = Owner.GetComponent(); + _sprite = Owner.GetComponent(); + _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(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(out var welder)) return false; + if (welder.TryUse(5.0f)) + { + // Repair generator + var damagable = Owner.GetComponent(); + var breakable = Owner.GetComponent(); + damagable.HealAllDamage(); + breakable.broken = false; + _intact = true; + + var entitySystemManager = IoCManager.Resolve(); + var notifyManager = IoCManager.Resolve(); + + entitySystemManager.GetEntitySystem().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 + } +} diff --git a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs index 3f451a2ce4..f0bdb1a615 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs @@ -45,6 +45,8 @@ namespace Content.Server.GameObjects private int StorageCapacityMax = 10000; public HashSet SubscribedSessions = new HashSet(); + public IReadOnlyCollection StoredEntities => storage.ContainedEntities; + public override void Initialize() { base.Initialize(); @@ -140,7 +142,6 @@ namespace Content.Server.GameObjects /// 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(out var placeableSurfaceComponent)) @@ -363,8 +364,10 @@ namespace Content.Server.GameObjects /// /// Inserts an entity into the storage component from the players active hand. /// - private bool PlayerInsertEntity(IEntity player) + public bool PlayerInsertEntity(IEntity player) { + _ensureInitialCalculated(); + if (!player.TryGetComponent(out IHandsComponent hands) || hands.GetActiveHand == null) return false; diff --git a/Content.Server/GameObjects/Components/Mobs/MindComponent.cs b/Content.Server/GameObjects/Components/Mobs/MindComponent.cs index 1cfc3d9991..8138dfb5d6 100644 --- a/Content.Server/GameObjects/Components/Mobs/MindComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/MindComponent.cs @@ -118,9 +118,17 @@ namespace Content.Server.GameObjects.Components.Mobs if (!ShowExamineInfo) return; + var dead = false; + + if(Owner.TryGetComponent(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]"); } diff --git a/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs b/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs index c26c109d70..14df66475a 100644 --- a/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/SpeciesComponent.cs @@ -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); } diff --git a/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs b/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs index f5e947d3d3..aa45e6a4b3 100644 --- a/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs @@ -90,6 +90,15 @@ namespace Content.Server.GameObjects.Components.Movement } } + /// + [ViewVariables] + public float CurrentPushSpeed => 5.0f; + + /// + [ViewVariables] + public float GrabRange => 0.2f; + + /// /// Is the entity Sprinting (running)? /// diff --git a/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs b/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs index f3229c82ac..f15f81f8fd 100644 --- a/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs @@ -67,6 +67,14 @@ namespace Content.Server.GameObjects.Components.Movement } } + /// + [ViewVariables] + public float CurrentPushSpeed => 5.0f; + + /// + [ViewVariables] + public float GrabRange => 0.2f; + /// /// Is the entity Sprinting (running)? /// diff --git a/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs b/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs index 9bc123c88a..ebbcae52ea 100644 --- a/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs @@ -34,6 +34,15 @@ namespace Content.Server.GameObjects.Components.Movement [ViewVariables(VVAccess.ReadWrite)] public float CurrentWalkSpeed { get; set; } = 8; public float CurrentSprintSpeed { get; set; } + + /// + [ViewVariables] + public float CurrentPushSpeed => 0.0f; + + /// + [ViewVariables] + public float GrabRange => 0.0f; + public bool Sprinting { get; set; } public Vector2 VelocityDir { get; } = Vector2.Zero; public GridCoordinates LastPosition { get; set; } diff --git a/Content.Server/GameObjects/Components/Sound/EmitSoundOnUseComponent].cs b/Content.Server/GameObjects/Components/Sound/EmitSoundOnUseComponent.cs similarity index 68% rename from Content.Server/GameObjects/Components/Sound/EmitSoundOnUseComponent].cs rename to Content.Server/GameObjects/Components/Sound/EmitSoundOnUseComponent.cs index 96059435a7..0d01ce2bb9 100644 --- a/Content.Server/GameObjects/Components/Sound/EmitSoundOnUseComponent].cs +++ b/Content.Server/GameObjects/Components/Sound/EmitSoundOnUseComponent.cs @@ -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().Play(_soundName, AudioHelpers.WithVariation(_pitchVariation).WithVolume(-2f)); + return true; + } Owner.GetComponent().Play(_soundName, AudioParams.Default.WithVolume(-2f)); return true; } diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs index e42c37978c..1dcf645020 100644 --- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs @@ -302,8 +302,8 @@ namespace Content.Server.GameObjects.EntitySystems var inputSys = EntitySystemManager.GetEntitySystem(); 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)) diff --git a/Content.Server/GameObjects/EntitySystems/GravitySystem.cs b/Content.Server/GameObjects/EntitySystems/GravitySystem.cs new file mode 100644 index 0000000000..1d60ddeec1 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/GravitySystem.cs @@ -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 _gridsToShake; + + private float internalTimer = 0.0f; + + public override void Initialize() + { + EntityQuery = new TypeEntityQuery(); + _gridsToShake = new Dictionary(); + } + + public override void Update(float frameTime) + { + internalTimer += frameTime; + var gridsWithGravity = new List(); + foreach (var entity in RelevantEntities) + { + var generator = entity.GetComponent(); + 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().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(_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); + } + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/HandsSystem.cs b/Content.Server/GameObjects/EntitySystems/HandsSystem.cs index 79d0048f76..855084abba 100644 --- a/Content.Server/GameObjects/EntitySystems/HandsSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/HandsSystem.cs @@ -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)); } /// @@ -126,7 +135,7 @@ namespace Content.Server.GameObjects.EntitySystems var interactionSystem = _entitySystemManager.GetEntitySystem(); - 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(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()); + } + } + } } } diff --git a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs index 65282a8a74..bdb05b95ce 100644 --- a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs @@ -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(); var physics = entity.GetComponent(); - - UpdateKinematics(entity.Transform, mover, physics); + if (entity.TryGetComponent(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(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(); // 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(); diff --git a/Content.Server/GameTicking/GamePreset.cs b/Content.Server/GameTicking/GamePreset.cs index b880b162a0..0075f7ae1b 100644 --- a/Content.Server/GameTicking/GamePreset.cs +++ b/Content.Server/GameTicking/GamePreset.cs @@ -1,11 +1,14 @@ -namespace Content.Server.GameTicking +using System.Collections.Generic; +using Robust.Server.Interfaces.Player; + +namespace Content.Server.GameTicking { /// /// A round-start setup preset, such as which antagonists to spawn. /// public abstract class GamePreset { - public abstract void Start(); + public abstract bool Start(IReadOnlyList players); public virtual string ModeTitle => "Sandbox"; public virtual string Description => "Secret!"; } diff --git a/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs b/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs index 5b866dc8fa..f45fa125b3 100644 --- a/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs +++ b/Content.Server/GameTicking/GamePresets/PresetDeathMatch.cs @@ -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 readyPlayers) { _gameTicker.AddGameRule(); + return true; } public override string ModeTitle => "Deathmatch"; diff --git a/Content.Server/GameTicking/GamePresets/PresetSandbox.cs b/Content.Server/GameTicking/GamePresets/PresetSandbox.cs index 05f15c6972..2eeab4a049 100644 --- a/Content.Server/GameTicking/GamePresets/PresetSandbox.cs +++ b/Content.Server/GameTicking/GamePresets/PresetSandbox.cs @@ -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 readyPlayers) { _sandboxManager.IsSandboxEnabled = true; + return true; } public override string ModeTitle => "Sandbox"; diff --git a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs new file mode 100644 index 0000000000..d4692cecf8 --- /dev/null +++ b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs @@ -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 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(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(); + 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?"; + } +} diff --git a/Content.Server/GameTicking/GameRules/RuleSuspicion.cs b/Content.Server/GameTicking/GameRules/RuleSuspicion.cs new file mode 100644 index 0000000000..91b16f3aaf --- /dev/null +++ b/Content.Server/GameTicking/GameRules/RuleSuspicion.cs @@ -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 +{ + /// + /// Simple GameRule that will do a free-for-all death match. + /// Kill everybody else to win. + /// + 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(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(out var mind)) + return; + + if (!mind.HasMind) + return; + + message.Species.Owner.Description += + mind.Mind.HasRole() ? "\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()) + 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()); + } + } +} diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 764c65633a..c95211205d 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -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 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("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().RealTime; _sendStatusToAll(); } @@ -255,15 +265,16 @@ namespace Content.Server.GameTicking var listOfPlayerInfo = new List(); foreach(var ply in _playerManager.GetAllPlayers().OrderBy(p => p.Name)) { - if(ply.AttachedEntity.TryGetComponent(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() }); diff --git a/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs b/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs index 5e3ed05180..9a5196ad3f 100644 --- a/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs +++ b/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs @@ -19,6 +19,17 @@ namespace Content.Server.Interfaces.GameObjects.Components.Movement /// float CurrentSprintSpeed { get; } + + /// + /// The movement speed (m/s) of the entity when it pushes off of a solid object in zero gravity. + /// + float CurrentPushSpeed { get; } + + /// + /// How far an entity can reach (in meters) to grab hold of a solid object in zero gravity. + /// + float GrabRange { get; } + /// /// Is the entity Sprinting (running)? /// diff --git a/Content.Server/Mobs/Mind.cs b/Content.Server/Mobs/Mind.cs index e931b80fbc..5e631f68ec 100644 --- a/Content.Server/Mobs/Mind.cs +++ b/Content.Server/Mobs/Mind.cs @@ -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() where T : Role + { + var t = typeof(T); + + return _roles.Any(role => role.GetType() == t); + } + /// /// Transfer this mind's control over to a new entity. /// diff --git a/Content.Server/Mobs/Role.cs b/Content.Server/Mobs/Role.cs index c7f0c617a7..f78b579f11 100644 --- a/Content.Server/Mobs/Role.cs +++ b/Content.Server/Mobs/Role.cs @@ -1,6 +1,9 @@ // Hey look, // Antag Datums. +using Content.Server.GameObjects.EntitySystems; +using Robust.Shared.Utility; + namespace Content.Server.Mobs { /// @@ -20,6 +23,11 @@ namespace Content.Server.Mobs /// public abstract string Name { get; } + /// + /// Whether this role should be considered antagonistic or not. + /// + public abstract bool Antag { get; } + protected Role(Mind mind) { Mind = mind; diff --git a/Content.Server/Mobs/Roles/Job.cs b/Content.Server/Mobs/Roles/Job.cs index 53c7ca721c..680a91ff20 100644 --- a/Content.Server/Mobs/Roles/Job.cs +++ b/Content.Server/Mobs/Roles/Job.cs @@ -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(); - 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!"); } } diff --git a/Content.Server/Mobs/Roles/SuspicionInnocentRole.cs b/Content.Server/Mobs/Roles/SuspicionInnocentRole.cs new file mode 100644 index 0000000000..177a8d3bbd --- /dev/null +++ b/Content.Server/Mobs/Roles/SuspicionInnocentRole.cs @@ -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(); + chat.DispatchServerMessage(Mind.Session, "You're an innocent!"); + } + } +} diff --git a/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs b/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs new file mode 100644 index 0000000000..65c1e10306 --- /dev/null +++ b/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs @@ -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(); + chat.DispatchServerMessage(Mind.Session, "You're a traitor!"); + } + } +} diff --git a/Content.Server/Mobs/Roles/Traitor.cs b/Content.Server/Mobs/Roles/Traitor.cs deleted file mode 100644 index 16804d497f..0000000000 --- a/Content.Server/Mobs/Roles/Traitor.cs +++ /dev/null @@ -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(); - chat.DispatchServerMessage( - Mind.Session, - "You're a traitor. Go fuck something up. Or something. I don't care to be honest."); - } - } -} diff --git a/Content.Server/Throw/ThrowHelper.cs b/Content.Server/Throw/ThrowHelper.cs index 0e402da225..77484fa656 100644 --- a/Content.Server/Throw/ThrowHelper.cs +++ b/Content.Server/Throw/ThrowHelper.cs @@ -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(); + var playerAccel = 5 * throwForce / (float) Math.Max(0.001, p.Mass); + p.LinearVelocity = Angle.FromDegrees(angle.Degrees + 180).ToVec() + * playerAccel / (1f / timing.TickRate); + } } } } diff --git a/Content.Shared/Audio/AudioHelpers.cs b/Content.Shared/Audio/AudioHelpers.cs new file mode 100644 index 0000000000..f6a920f479 --- /dev/null +++ b/Content.Shared/Audio/AudioHelpers.cs @@ -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{ + /// + /// Returns a random pitch. + /// + public static AudioParams WithVariation(float amplitude) + { + var scale = (float)(IoCManager.Resolve().NextGaussian(1, amplitude)); + return AudioParams.Default.WithPitchScale(scale); + } + } +} diff --git a/Content.Shared/EntryPoint.cs b/Content.Shared/EntryPoint.cs index a9df20ff2a..f2a1e7312e 100644 --- a/Content.Shared/EntryPoint.cs +++ b/Content.Shared/EntryPoint.cs @@ -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() diff --git a/Content.Shared/GameObjects/Components/Gravity/SharedGravityGeneratorComponent.cs b/Content.Shared/GameObjects/Components/Gravity/SharedGravityGeneratorComponent.cs new file mode 100644 index 0000000000..344a6f68ce --- /dev/null +++ b/Content.Shared/GameObjects/Components/Gravity/SharedGravityGeneratorComponent.cs @@ -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; + + /// + /// Sent to the server to set whether the generator should be on or off + /// + [Serializable, NetSerializable] + public class SwitchGeneratorMessage : BoundUserInterfaceMessage + { + public bool On; + + public SwitchGeneratorMessage(bool on) + { + On = on; + } + } + + /// + /// Sent to the server when requesting the status of the generator + /// + [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 + } + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index f9832cc751..854628acc0 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -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; } } diff --git a/Content.Shared/Input/ContentKeyFunctions.cs b/Content.Shared/Input/ContentKeyFunctions.cs index a0ae771de3..d44cb1d64d 100644 --- a/Content.Shared/Input/ContentKeyFunctions.cs +++ b/Content.Shared/Input/ContentKeyFunctions.cs @@ -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"; } } diff --git a/Content.Shared/SharedGameTicker.cs b/Content.Shared/SharedGameTicker.cs index 38d3e0985e..565a39ee70 100644 --- a/Content.Shared/SharedGameTicker.cs +++ b/Content.Shared/SharedGameTicker.cs @@ -138,7 +138,7 @@ namespace Content.Shared public string GamemodeTitle; public TimeSpan RoundDuration; - + public uint PlayerCount; diff --git a/Jenkinsfile b/Jenkinsfile index ca927d5a78..5fc5bacb9e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -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') { diff --git a/Resources/Audio/effects/alert.ogg b/Resources/Audio/effects/alert.ogg new file mode 100644 index 0000000000..69bc52bdb8 Binary files /dev/null and b/Resources/Audio/effects/alert.ogg differ diff --git a/Resources/Groups/groups.yml b/Resources/Groups/groups.yml index 50b84be17f..7e3688ef71 100644 --- a/Resources/Groups/groups.yml +++ b/Resources/Groups/groups.yml @@ -134,3 +134,4 @@ - gc_mode CanViewVar: true CanAdminPlace: true + CanScript: true diff --git a/Resources/Locale/nl-NL/tools.yml b/Resources/Locale/nl-NL/tools.yml new file mode 100644 index 0000000000..6a9f974c3f --- /dev/null +++ b/Resources/Locale/nl-NL/tools.yml @@ -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. diff --git a/Resources/Maps/stationstation.yml b/Resources/Maps/stationstation.yml index e7402cdbde..3084a36a4e 100644 --- a/Resources/Maps/stationstation.yml +++ b/Resources/Maps/stationstation.yml @@ -62,8 +62,9 @@ tilemap: 55: floor_steel_dirty 56: floor_techmaint 57: floor_white - 58: plating - 59: underplating + 58: floor_wood + 59: plating + 60: underplating grids: - settings: chunksize: 16 @@ -71,29 +72,29 @@ grids: snapsize: 1 chunks: - ind: "-1,0" - tiles: NgAAADYAAAA2AAAANgAAADYAAAA7AAAAOwAAADsAAAA7AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAOwAAADgAAAA7AAAAOwAAADsAAAA7AAAANgAAADYAAAA2AAAANgAAADsAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA7AAAAOwAAADsAAAA7AAAAOwAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAAAAAAANgAAADYAAAA2AAAAAAAAAAAAAAAAAAAAAAAAADsAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAAAAAADYAAAA2AAAANgAAAAAAAAAAAAAAAAAAAAAAAAA7AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: NgAAADYAAAA2AAAANgAAADYAAAA8AAAAPAAAADwAAAA8AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAPAAAADgAAAA8AAAAPAAAADwAAAA8AAAANgAAADYAAAA2AAAANgAAADwAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA8AAAAPAAAADwAAAA8AAAAPAAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAAAAAAANgAAADYAAAA2AAAAAAAAAAAAAAAAAAAAAAAAADwAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAAAAAADYAAAA2AAAANgAAAAAAAAAAAAAAAAAAAAAAAAA8AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - ind: "-1,-1" - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA7AAAAOwAAADgAAAA4AAAAOAAAADgAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7AAAAOwAAADsAAAA4AAAAOAAAADsAAAA4AAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA7AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7AAAAOwAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADYAAAA2AAAANgAAADYAAAA2AAAAOwAAADsAAAA7AAAAOwAAADsAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADsAAAA4AAAAOwAAADsAAAA7AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADsAAAA7AAAAOwAAADsAAAA4AAAAOwAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADYAAAA2AAAANgAAADYAAAA2AAAAOwAAADgAAAA7AAAAOwAAADsAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA2AAAANgAAADYAAAA2AAAANgAAADsAAAA4AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAANgAAADYAAAA2AAAANgAAADYAAAA7AAAAOAAAADsAAAA7AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAOwAAADgAAAA7AAAAOwAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADsAAAA4AAAAOwAAADsAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA7AAAAOAAAADsAAAA7AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA8AAAAPAAAADgAAAA4AAAAOAAAADgAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAPAAAADwAAAA4AAAAOAAAADwAAAA4AAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA8AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAPAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADYAAAA2AAAANgAAADYAAAA2AAAAPAAAADwAAAA8AAAAPAAAADwAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADwAAAA4AAAAPAAAADwAAAA8AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADwAAAA8AAAAPAAAADwAAAA4AAAAPAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADYAAAA2AAAANgAAADYAAAA2AAAAPAAAADgAAAA8AAAAPAAAADwAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA2AAAANgAAADYAAAA2AAAANgAAADwAAAA4AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAANgAAADYAAAA2AAAANgAAADYAAAA8AAAAOAAAADwAAAA8AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAPAAAADgAAAA8AAAAPAAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADwAAAA4AAAAPAAAADwAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA8AAAAOAAAADwAAAA8AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAAA== - ind: "-1,1" tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - ind: "0,1" - tiles: AAAAADsAAAA2AAAANgAAADsAAAA7AAAAOwAAADsAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA7AAAANgAAAAAAAAA7AAAANgAAADYAAAA7AAAAOAAAADsAAAA7AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOwAAADkAAAAAAAAAOwAAADYAAAA2AAAAOwAAADgAAAA7AAAAOwAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADsAAAA7AAAAAAAAADsAAAA2AAAANgAAADsAAAA4AAAAOwAAADsAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA7AAAAOQAAAAAAAAA7AAAANgAAADYAAAA7AAAAOAAAADsAAAA4AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAAAAAAAOwAAADYAAAA2AAAAOwAAADgAAAA7AAAAOwAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADsAAAA5AAAAAAAAADsAAAA2AAAANgAAADsAAAA4AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOQAAAAAAAAA7AAAANgAAADYAAAA7AAAAOAAAADsAAAA4AAAAOAAAADgAAAA7AAAAOAAAADgAAAA7AAAAOwAAADkAAAAAAAAAOwAAADYAAAA2AAAAOwAAADgAAAA7AAAAOAAAADgAAAA4AAAAOwAAADgAAAA4AAAAOwAAADkAAAA5AAAAAAAAAAAAAAAAAAAAAAAAADsAAAA4AAAAOwAAADgAAAA4AAAAOAAAADsAAAA4AAAAOAAAADsAAAA7AAAAOQAAAAAAAAAAAAAAAAAAAAAAAAA7AAAAOAAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADkAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADgAAAA7AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA5AAAAAAAAAAAAAAAAAAAAAAAAADsAAAA4AAAAOwAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7AAAAOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: AAAAADwAAAA2AAAANgAAADwAAAA8AAAAPAAAADwAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA8AAAANgAAAAAAAAA8AAAANgAAADYAAAA8AAAAOAAAADwAAAA8AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAPAAAADkAAAAAAAAAPAAAADYAAAA2AAAAPAAAADgAAAA8AAAAPAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADwAAAA8AAAAAAAAADwAAAA2AAAANgAAADwAAAA4AAAAPAAAADwAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA8AAAAOQAAAAAAAAA8AAAANgAAADYAAAA8AAAAOAAAADwAAAA4AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAAAAAAAPAAAADYAAAA2AAAAPAAAADgAAAA8AAAAPAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADwAAAA5AAAAAAAAADwAAAA2AAAANgAAADwAAAA4AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAOQAAAAAAAAA8AAAANgAAADYAAAA8AAAAOAAAADwAAAA4AAAAOAAAADgAAAA8AAAAOAAAADgAAAA8AAAAPAAAADkAAAAAAAAAPAAAADYAAAA2AAAAPAAAADgAAAA8AAAAOAAAADgAAAA4AAAAPAAAADgAAAA4AAAAPAAAADkAAAA5AAAAAAAAAAAAAAAAAAAAAAAAADwAAAA4AAAAPAAAADgAAAA4AAAAOAAAADwAAAA4AAAAOAAAADwAAAA8AAAAOQAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAOAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADkAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADgAAAA8AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA5AAAAAAAAAAAAAAAAAAAAAAAAADwAAAA4AAAAPAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - ind: "0,0" - tiles: NgAAADsAAAA2AAAANgAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADkAAAA5AAAAOQAAADsAAAA7AAAANgAAADYAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA5AAAAOQAAADkAAAA2AAAANgAAADYAAAA2AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7AAAAOQAAADkAAAA5AAAANgAAADYAAAA2AAAANgAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADsAAAA7AAAAOQAAADYAAAA2AAAANgAAADYAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7AAAAOQAAADkAAAA2AAAANgAAADYAAAA2AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADkAAAA5AAAANgAAADYAAAA2AAAANgAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA5AAAAOQAAADYAAAA2AAAANgAAADYAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7AAAAOQAAADkAAAA7AAAAOwAAADYAAAA2AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADkAAAA5AAAAOwAAADsAAAA2AAAANgAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA5AAAAOQAAADsAAAA4AAAANgAAADYAAAA4AAAAOwAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADsAAAA4AAAAOQAAADkAAAA7AAAAOwAAADYAAAA2AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA2AAAAAAAAADsAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAAAAAAAA7AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAAAAAAAOwAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAAAAAADsAAAA2AAAANgAAADsAAAA4AAAAOwAAADsAAAA7AAAAOQAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAAA== + tiles: NgAAADwAAAA2AAAANgAAADwAAAA4AAAAOAAAADgAAAA8AAAAAAAAAAAAAAAAAAAAPAAAADkAAAA5AAAAOQAAADwAAAA8AAAANgAAADYAAAA8AAAAOAAAADgAAAA4AAAAPAAAAAAAAAAAAAAAAAAAADwAAAA5AAAAOQAAADkAAAA2AAAANgAAADYAAAA2AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAAAAAAAA8AAAAOQAAADkAAAA5AAAANgAAADYAAAA2AAAANgAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADwAAAA8AAAAOQAAADYAAAA2AAAANgAAADYAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAOQAAADkAAAA2AAAANgAAADYAAAA2AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADkAAAA5AAAANgAAADYAAAA2AAAANgAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA5AAAAOQAAADYAAAA2AAAANgAAADYAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAOQAAADkAAAA8AAAAPAAAADYAAAA2AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADkAAAA5AAAAPAAAADwAAAA2AAAANgAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA5AAAAOQAAADwAAAA4AAAANgAAADYAAAA4AAAAPAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADwAAAA4AAAAOQAAADkAAAA8AAAAPAAAADYAAAA2AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA2AAAAAAAAADwAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAAAAAAAA8AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAAAAAAAPAAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAAAAAADwAAAA2AAAANgAAADwAAAA4AAAAPAAAADwAAAA8AAAAOQAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAAA== - ind: "0,-1" - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAOAAAADgAAAA4AAAAOwAAADsAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAADsAAAA4AAAAOAAAADsAAAA7AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAOAAAADgAAAA4AAAAOwAAADsAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAADgAAAA4AAAAOAAAADsAAAA7AAAAOwAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAA4AAAAOAAAADgAAAA7AAAAOAAAADgAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAOAAAADgAAAA4AAAAOAAAADsAAAA7AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA7AAAAOwAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7AAAAOQAAADkAAAA7AAAAOwAAADgAAAA4AAAAOwAAADsAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADkAAAA5AAAANgAAADsAAAA2AAAANgAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADsAAAA5AAAAOQAAADYAAAA2AAAANgAAADYAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7AAAAOQAAADkAAAA2AAAANgAAADYAAAA2AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADkAAAA5AAAANgAAADYAAAA2AAAANgAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADsAAAA7AAAAOQAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAOAAAADgAAAA4AAAAPAAAADwAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAADwAAAA4AAAAOAAAADwAAAA8AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAOAAAADgAAAA4AAAAPAAAADwAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAADgAAAA4AAAAOAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAA4AAAAOAAAADgAAAA8AAAAOAAAADgAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAOAAAADgAAAA4AAAAOAAAADwAAAA8AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAADgAAAA4AAAAOAAAADgAAAA4AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA8AAAAPAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAOQAAADkAAAA8AAAAPAAAADgAAAA4AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADkAAAA5AAAANgAAADwAAAA2AAAANgAAADwAAAA4AAAAOAAAADgAAAA8AAAAAAAAAAAAAAAAAAAAAAAAADwAAAA5AAAAOQAAADYAAAA2AAAANgAAADYAAAA8AAAAOAAAADgAAAA4AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAOQAAADkAAAA2AAAANgAAADYAAAA2AAAAPAAAADgAAAA4AAAAOAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADkAAAA5AAAANgAAADYAAAA2AAAANgAAADwAAAA4AAAAOAAAADgAAAA8AAAAAAAAAAAAAAAAAAAAPAAAADwAAAA8AAAAOQAAAA== - ind: "1,-1" - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA7AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOwAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA7AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOwAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADkAAAA5AAAAOwAAADsAAAA7AAAAOQAAADsAAAA7AAAAOwAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA8AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAPAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA8AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAPAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADkAAAA5AAAAPAAAADwAAAA8AAAAOQAAADwAAAA8AAAAPAAAAA== - ind: "-2,0" tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - ind: "-2,-1" tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAA== - ind: "1,0" - tiles: OQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA7AAAAAAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOwAAAAAAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADsAAAAAAAAAOQAAADsAAAA7AAAAOwAAADkAAAA7AAAAOwAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA7AAAAAAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADsAAAA7AAAAOQAAADsAAAA5AAAAOwAAADsAAAA7AAAAOwAAAAAAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA7AAAAOQAAADkAAAA5AAAAOQAAADkAAAA7AAAAAAAAAAAAAAAAAAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOwAAADkAAAA5AAAAOQAAADkAAAA5AAAAOwAAAAAAAAAAAAAAAAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADsAAAA5AAAAOQAAADkAAAA5AAAAOQAAADsAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA7AAAAAAAAAAAAAAAAAAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOwAAADkAAAA5AAAAOQAAADkAAAA5AAAAOwAAAAAAAAAAAAAAAAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADsAAAA5AAAAOQAAADkAAAA5AAAAOQAAADsAAAAAAAAAAAAAAAAAAAA2AAAANgAAADYAAAA2AAAANgAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAAAAAAAAAAAAAAAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAADsAAAA7AAAAOwAAAAAAAAAAAAAAAAAAAA== + tiles: OQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA8AAAAAAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAPAAAAAAAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADwAAAAAAAAAOQAAADwAAAA8AAAAPAAAADkAAAA8AAAAPAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA8AAAAAAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADwAAAA8AAAAOQAAADwAAAA5AAAAPAAAADwAAAA8AAAAPAAAAAAAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA8AAAAOQAAADkAAAA5AAAAOQAAADkAAAA8AAAAAAAAAAAAAAAAAAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAPAAAADkAAAA5AAAAOQAAADkAAAA5AAAAPAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADwAAAA5AAAAOQAAADkAAAA5AAAAOQAAADwAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADkAAAA8AAAAAAAAAAAAAAAAAAAAOQAAADkAAAA5AAAAOQAAADkAAAA5AAAAPAAAADkAAAA5AAAAOQAAADkAAAA5AAAAPAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAOQAAADkAAAA5AAAAOQAAADwAAAA5AAAAOQAAADkAAAA5AAAAOQAAADwAAAAAAAAAAAAAAAAAAAA2AAAANgAAADYAAAA2AAAANgAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAAAAAAAAAAAAAAAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAANgAAADYAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAAAAAAAAAAAAAAAAAAA== - ind: "2,-1" - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADsAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAAADkAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOwAAADsAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADwAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAAADkAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAADwAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - ind: "1,1" - tiles: NgAAADYAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOwAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAAADkAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAAADkAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAAADkAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAAADkAAAA7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: NgAAADYAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAPAAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAAADkAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAAADkAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAAADkAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkAAAA5AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5AAAAOQAAADwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAAADkAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== entities: - uid: 0 components: @@ -289,14 +290,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 30 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 27 type: MetalStack components: @@ -320,14 +321,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 170 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 30 type: LightBulb components: @@ -768,6 +769,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 92 type: Wire components: @@ -1177,6 +1180,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 148 type: MedkitFilled components: @@ -1219,6 +1224,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 151 type: FireExtinguisher components: @@ -1663,14 +1670,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 211 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 213 type: ChairOfficeLight components: @@ -1694,14 +1701,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 316 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 216 type: MetalStack components: @@ -1953,14 +1960,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 222 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 252 type: solid_wall components: @@ -2150,9 +2157,6 @@ entities: entities: [] type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer - - magazines: - - A10mmSMG - type: BallisticMagazineWeapon - uid: 270 type: SmgC20r components: @@ -2168,9 +2172,6 @@ entities: entities: [] type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer - - magazines: - - A10mmSMG - type: BallisticMagazineWeapon - uid: 271 type: solid_wall components: @@ -2284,12 +2285,19 @@ entities: rot: -1.5707963267949 rad type: Transform - uid: 287 - type: solid_wall + type: LockerScience components: - parent: 0 - pos: 4.5,-2.5 - rot: -1.5707963267949 rad + pos: 13.5,21.5 + rot: -1.5707963267948966 rad type: Transform + - containers: + EntityStorageComponent: + entities: [] + type: Robust.Server.GameObjects.Components.Container.Container + type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 288 type: solid_wall components: @@ -2502,14 +2510,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 318 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 318 type: LightTube components: @@ -2524,14 +2532,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 320 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 320 type: LightTube components: @@ -2552,14 +2560,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 323 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 323 type: LightBulb components: @@ -2857,6 +2865,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 365 type: LockerToolFilled components: @@ -2869,6 +2879,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 366 type: LockerToolFilled components: @@ -2881,6 +2893,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 367 type: LockerToolFilled components: @@ -2893,6 +2907,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 368 type: LockerToolFilled components: @@ -2905,6 +2921,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 369 type: GlassStack components: @@ -2926,21 +2944,6 @@ entities: pos: -14.5,-7.5 rot: -1.5707963267949 rad type: Transform - - recipes: - - Brutepack - - Ointment - - LightTube - - LightBulb - - MetalStack - - GlassStack - - Wirecutter - - Screwdriver - - Welder - - Wrench - - CableStack - - Crowbar - - Multitool - type: LatheDatabase - uid: 372 type: Autolathe components: @@ -2948,21 +2951,6 @@ entities: pos: -13.5,-7.5 rot: -1.5707963267949 rad type: Transform - - recipes: - - Brutepack - - Ointment - - LightTube - - LightBulb - - MetalStack - - GlassStack - - Wirecutter - - Screwdriver - - Welder - - Wrench - - CableStack - - Crowbar - - Multitool - type: LatheDatabase - uid: 373 type: Autolathe components: @@ -2970,21 +2958,6 @@ entities: pos: -12.5,-7.5 rot: -1.5707963267949 rad type: Transform - - recipes: - - Brutepack - - Ointment - - LightTube - - LightBulb - - MetalStack - - GlassStack - - Wirecutter - - Screwdriver - - Welder - - Wrench - - CableStack - - Crowbar - - Multitool - type: LatheDatabase - uid: 374 type: Poweredlight components: @@ -2994,14 +2967,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 375 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 375 type: LightTube components: @@ -3016,14 +2989,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 377 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 377 type: LightTube components: @@ -3037,14 +3010,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 379 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 379 type: LightTube components: @@ -3058,14 +3031,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 381 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 381 type: LightTube components: @@ -3079,14 +3052,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 383 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 383 type: LightTube components: @@ -3101,14 +3074,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 385 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 385 type: LightBulb components: @@ -3165,14 +3138,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 393 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 393 type: LightTube components: @@ -4925,14 +4898,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 642 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 642 type: LightTube components: @@ -4947,14 +4920,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 644 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 644 type: LightTube components: @@ -4969,14 +4942,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 646 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 646 type: LightTube components: @@ -5089,14 +5062,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 664 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 664 type: LightTube components: @@ -5111,14 +5084,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 666 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 666 type: LightTube components: @@ -5147,14 +5120,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 670 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 670 type: LightTube components: @@ -5379,14 +5352,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 702 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 702 type: LightTube components: @@ -5401,14 +5374,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 704 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 704 type: LightTube components: @@ -5479,14 +5452,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 714 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 714 type: LightTube components: @@ -5568,14 +5541,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 723 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 723 type: LightTube components: @@ -5726,6 +5699,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 744 type: CrateMedical components: @@ -5738,6 +5713,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 745 type: LockerMedical components: @@ -5750,6 +5727,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 746 type: LockerMedical components: @@ -5762,6 +5741,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 747 type: LockerMedical components: @@ -5774,6 +5755,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 748 type: LockerChemistry components: @@ -5786,6 +5769,8 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 749 type: solid_wall components: @@ -6677,14 +6662,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 876 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 876 type: LightTube components: @@ -6699,14 +6684,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 878 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 878 type: LightTube components: @@ -6726,14 +6711,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 879 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 881 type: Wire components: @@ -6757,14 +6742,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 884 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 884 type: LightTube components: @@ -6779,14 +6764,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 886 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 886 type: LightTube components: @@ -6800,14 +6785,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 888 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 888 type: LightBulb components: @@ -6821,14 +6806,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 890 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 890 type: LightTube components: @@ -6847,14 +6832,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 891 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 893 type: PoweredSmallLight components: @@ -6864,14 +6849,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 894 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 894 type: LightBulb components: @@ -6893,14 +6878,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 897 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 897 type: LightBulb components: @@ -6915,16 +6900,12 @@ entities: type: Transform - points: 343000 type: ResearchServer - - technologies: [] - type: TechnologyDatabase - uid: 899 type: ComputerResearchAndDevelopment components: - parent: 0 pos: 8.5,18.5 type: Transform - - technologies: [] - type: TechnologyDatabase - uid: 900 type: BaseResearchAndDevelopmentPointSource components: @@ -6937,66 +6918,18 @@ entities: - parent: 0 pos: 8.5,17.5 type: Transform - - recipes: [] - protolatherecipes: - - Brutepack - - Ointment - - LightTube - - LightBulb - - MetalStack - - GlassStack - - Wirecutter - - Screwdriver - - Welder - - Wrench - - CableStack - - Crowbar - - Multitool - type: ProtolatheDatabase - - technologies: [] - type: TechnologyDatabase - uid: 902 type: Autolathe components: - parent: 0 pos: 13.5,18.5 type: Transform - - recipes: - - Brutepack - - Ointment - - LightTube - - LightBulb - - MetalStack - - GlassStack - - Wirecutter - - Screwdriver - - Welder - - Wrench - - CableStack - - Crowbar - - Multitool - type: LatheDatabase - uid: 903 type: Autolathe components: - parent: 0 pos: -4.5,-5.5 type: Transform - - recipes: - - Brutepack - - Ointment - - LightTube - - LightBulb - - MetalStack - - GlassStack - - Wirecutter - - Screwdriver - - Welder - - Wrench - - CableStack - - Crowbar - - Multitool - type: LatheDatabase - uid: 904 type: Table components: @@ -7250,14 +7183,14 @@ entities: type: Transform - color: '#FFFFFFFF' type: PointLight - - load: 40 - type: PowerDevice - containers: light_bulb: entities: - 940 type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice - uid: 940 type: LightTube components: @@ -7343,16 +7276,126 @@ entities: entities: [] type: Robust.Server.GameObjects.Components.Container.Container type: ContainerContainer + - IsPlaceable: False + type: PlaceableSurface - uid: 953 - type: LockerScience + type: solid_wall components: - parent: 0 - pos: 13.5,21.5 + pos: 5.5,2.5 rot: -1.5707963267948966 rad type: Transform +- uid: 954 + type: solid_wall + components: + - parent: 0 + pos: 6.5,2.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 955 + type: solid_wall + components: + - parent: 0 + pos: 7.5,2.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 956 + type: solid_wall + components: + - parent: 0 + pos: 8.5,2.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 957 + type: solid_wall + components: + - parent: 0 + pos: 8.5,1.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 958 + type: solid_wall + components: + - parent: 0 + pos: 8.5,0.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 959 + type: solid_wall + components: + - parent: 0 + pos: 8.5,-0.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 960 + type: solid_wall + components: + - parent: 0 + pos: 8.5,-1.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 961 + type: solid_wall + components: + - parent: 0 + pos: 8.5,-2.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 962 + type: solid_wall + components: + - parent: 0 + pos: 8.5,-3.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 963 + type: solid_wall + components: + - parent: 0 + pos: 8.5,-4.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 964 + type: solid_wall + components: + - parent: 0 + pos: 7.5,-4.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 965 + type: GravityGenerator + components: + - parent: 0 + pos: 6.5,0.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 966 + type: Poweredlight + components: + - parent: 0 + pos: 8,-1.5 + rot: 3.141592653589793 rad + type: Transform + - color: '#FFFFFFFF' + type: PointLight - containers: - EntityStorageComponent: - entities: [] - type: Robust.Server.GameObjects.Components.Container.Container + light_bulb: + entities: + - 967 + type: Content.Server.GameObjects.ContainerSlot type: ContainerContainer + - load: 40 + type: PowerDevice +- uid: 967 + type: LightTube + components: + - parent: 966 + type: Transform +- uid: 968 + type: airlock_engineering + components: + - parent: 0 + pos: 4.5,-2.5 + rot: -1.5707963267948966 rad + type: Transform ... diff --git a/Resources/Prototypes/Entities/Buildings/airlock_types.yml b/Resources/Prototypes/Entities/Buildings/airlock_types.yml index c1c50f00b3..d3b2f17098 100644 --- a/Resources/Prototypes/Entities/Buildings/airlock_types.yml +++ b/Resources/Prototypes/Entities/Buildings/airlock_types.yml @@ -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 diff --git a/Resources/Prototypes/Entities/Buildings/gravity_generator.yml b/Resources/Prototypes/Entities/Buildings/gravity_generator.yml new file mode 100644 index 0000000000..19fc4f3915 --- /dev/null +++ b/Resources/Prototypes/Entities/Buildings/gravity_generator.yml @@ -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 diff --git a/Resources/Prototypes/Entities/Buildings/medical_scanner.yml b/Resources/Prototypes/Entities/Buildings/medical_scanner.yml index aae8cefd16..611fd803ed 100644 --- a/Resources/Prototypes/Entities/Buildings/medical_scanner.yml +++ b/Resources/Prototypes/Entities/Buildings/medical_scanner.yml @@ -36,7 +36,6 @@ - type: Appearance visuals: - type: MedicalScannerVisualizer2D - - type: PowerDevice - type: UserInterface interfaces: - key: enum.MedicalScannerUiKey.Key diff --git a/Resources/Prototypes/Entities/Items/Clothing/belts.yml b/Resources/Prototypes/Entities/Items/Clothing/belts.yml index b2423d740e..2994275e00 100644 --- a/Resources/Prototypes/Entities/Items/Clothing/belts.yml +++ b/Resources/Prototypes/Entities/Items/Clothing/belts.yml @@ -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 diff --git a/Resources/Prototypes/Entities/Items/Instruments.yml b/Resources/Prototypes/Entities/Items/Instruments.yml index ccfd97dd8d..08db84188f 100644 --- a/Resources/Prototypes/Entities/Items/Instruments.yml +++ b/Resources/Prototypes/Entities/Items/Instruments.yml @@ -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 diff --git a/Resources/Prototypes/Entities/Items/bike_horn.yml b/Resources/Prototypes/Entities/Items/bike_horn.yml index 51d72315ae..8314634c1c 100644 --- a/Resources/Prototypes/Entities/Items/bike_horn.yml +++ b/Resources/Prototypes/Entities/Items/bike_horn.yml @@ -20,6 +20,7 @@ - type: Sound - type: EmitSoundOnUse sound: /Audio/items/bikehorn.ogg + variation: 0.2 - type: UseDelay delay: 0.5 diff --git a/Resources/Prototypes/Entities/Weapons/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Weapons/Shotguns/shotguns.yml index 00552cf73c..e99f10a3b6 100644 --- a/Resources/Prototypes/Entities/Weapons/Shotguns/shotguns.yml +++ b/Resources/Prototypes/Entities/Weapons/Shotguns/shotguns.yml @@ -42,6 +42,3 @@ - type: Item Size: 24 sprite: Objects/Guns/SMGs/c20r.rsi - - type: Item - Size: 24 - sprite: Objects/Guns/SMGs/wt550.rsi diff --git a/Resources/Prototypes/Entities/janitor.yml b/Resources/Prototypes/Entities/janitor.yml index 0cc504dd1f..ca1add7b83 100644 --- a/Resources/Prototypes/Entities/janitor.yml +++ b/Resources/Prototypes/Entities/janitor.yml @@ -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 diff --git a/Resources/Prototypes/LatheRecipes/tools.yml b/Resources/Prototypes/LatheRecipes/tools.yml index 0480b2cb24..38f0139b16 100644 --- a/Resources/Prototypes/LatheRecipes/tools.yml +++ b/Resources/Prototypes/LatheRecipes/tools.yml @@ -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: diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/closed.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/closed.png new file mode 100644 index 0000000000..a28a608d31 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/closed.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/closed_unlit.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/closed_unlit.png new file mode 100644 index 0000000000..4f74aa619a Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/closed_unlit.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/closing.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/closing.png new file mode 100644 index 0000000000..7201eaf83a Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/closing.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/closing_unlit.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/closing_unlit.png new file mode 100644 index 0000000000..3d666ac1d1 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/closing_unlit.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/deny.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/deny.png new file mode 100644 index 0000000000..7be2da4d41 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/deny.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/locked.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/locked.png new file mode 100644 index 0000000000..cf6e017161 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/locked.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/meta.json b/Resources/Textures/Buildings/Doors/airlock_security.rsi/meta.json new file mode 100644 index 0000000000..5d35cc5738 --- /dev/null +++ b/Resources/Textures/Buildings/Doors/airlock_security.rsi/meta.json @@ -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 + ] + ] + } + ] +} diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/open.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/open.png new file mode 100644 index 0000000000..3ffd2a6ff5 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/open.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/opening.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/opening.png new file mode 100644 index 0000000000..b74258c7d7 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/opening.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/opening_unlit.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/opening_unlit.png new file mode 100644 index 0000000000..d281c519de Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/opening_unlit.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/panel_closing.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/panel_closing.png new file mode 100644 index 0000000000..edb14f460c Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/panel_closing.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/panel_open.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/panel_open.png new file mode 100644 index 0000000000..5f3bfeae15 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/panel_open.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/panel_opening.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/panel_opening.png new file mode 100644 index 0000000000..c5970003dc Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/panel_opening.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/spark.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/spark.png new file mode 100644 index 0000000000..9207f1f8b5 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/spark.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/sparks_broken.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/sparks_broken.png new file mode 100644 index 0000000000..98c9278337 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/sparks_broken.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/sparks_damaged.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/sparks_damaged.png new file mode 100644 index 0000000000..415c8b1f5a Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/sparks_damaged.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/sparks_open.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/sparks_open.png new file mode 100644 index 0000000000..9e48763e6c Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/sparks_open.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security.rsi/welded.png b/Resources/Textures/Buildings/Doors/airlock_security.rsi/welded.png new file mode 100644 index 0000000000..bd03f8b62e Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security.rsi/welded.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closed.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closed.png new file mode 100644 index 0000000000..76087c0b03 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closed.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closed_unlit.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closed_unlit.png new file mode 100644 index 0000000000..4f74aa619a Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closed_unlit.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closing.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closing.png new file mode 100644 index 0000000000..6bd2a13377 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closing.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closing_unlit.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closing_unlit.png new file mode 100644 index 0000000000..3d666ac1d1 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/closing_unlit.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/deny.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/deny.png new file mode 100644 index 0000000000..6ec1bc8e65 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/deny.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/locked.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/locked.png new file mode 100644 index 0000000000..9fdd2c42f0 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/locked.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/meta.json b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/meta.json new file mode 100644 index 0000000000..5d35cc5738 --- /dev/null +++ b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/meta.json @@ -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 + ] + ] + } + ] +} diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/open.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/open.png new file mode 100644 index 0000000000..3ffd2a6ff5 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/open.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/opening.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/opening.png new file mode 100644 index 0000000000..a6f372d40b Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/opening.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/opening_unlit.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/opening_unlit.png new file mode 100644 index 0000000000..d281c519de Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/opening_unlit.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/panel_closing.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/panel_closing.png new file mode 100644 index 0000000000..edb14f460c Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/panel_closing.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/panel_open.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/panel_open.png new file mode 100644 index 0000000000..5f3bfeae15 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/panel_open.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/panel_opening.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/panel_opening.png new file mode 100644 index 0000000000..c5970003dc Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/panel_opening.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/spark.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/spark.png new file mode 100644 index 0000000000..3901a24af9 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/spark.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/sparks_broken.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/sparks_broken.png new file mode 100644 index 0000000000..98c9278337 Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/sparks_broken.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/sparks_damaged.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/sparks_damaged.png new file mode 100644 index 0000000000..415c8b1f5a Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/sparks_damaged.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/sparks_open.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/sparks_open.png new file mode 100644 index 0000000000..9e48763e6c Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/sparks_open.png differ diff --git a/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/welded.png b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/welded.png new file mode 100644 index 0000000000..bd03f8b62e Binary files /dev/null and b/Resources/Textures/Buildings/Doors/airlock_security_glass.rsi/welded.png differ diff --git a/Resources/Textures/Buildings/gravity_generator.rsi/broken.png b/Resources/Textures/Buildings/gravity_generator.rsi/broken.png new file mode 100644 index 0000000000..0e02745bb8 Binary files /dev/null and b/Resources/Textures/Buildings/gravity_generator.rsi/broken.png differ diff --git a/Resources/Textures/Buildings/gravity_generator.rsi/meta.json b/Resources/Textures/Buildings/gravity_generator.rsi/meta.json new file mode 100755 index 0000000000..4d6f697ad5 --- /dev/null +++ b/Resources/Textures/Buildings/gravity_generator.rsi/meta.json @@ -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 + } + ] +} diff --git a/Resources/Textures/Buildings/gravity_generator.rsi/off.png b/Resources/Textures/Buildings/gravity_generator.rsi/off.png new file mode 100644 index 0000000000..9dd0406e39 Binary files /dev/null and b/Resources/Textures/Buildings/gravity_generator.rsi/off.png differ diff --git a/Resources/Textures/Buildings/gravity_generator.rsi/on.png b/Resources/Textures/Buildings/gravity_generator.rsi/on.png new file mode 100755 index 0000000000..d887cea52a Binary files /dev/null and b/Resources/Textures/Buildings/gravity_generator.rsi/on.png differ diff --git a/Resources/Textures/Buildings/gravity_generator_core.rsi/activated.png b/Resources/Textures/Buildings/gravity_generator_core.rsi/activated.png new file mode 100644 index 0000000000..18aaf0103c Binary files /dev/null and b/Resources/Textures/Buildings/gravity_generator_core.rsi/activated.png differ diff --git a/Resources/Textures/Buildings/gravity_generator_core.rsi/activating.png b/Resources/Textures/Buildings/gravity_generator_core.rsi/activating.png new file mode 100644 index 0000000000..24abe04528 Binary files /dev/null and b/Resources/Textures/Buildings/gravity_generator_core.rsi/activating.png differ diff --git a/Resources/Textures/Buildings/gravity_generator_core.rsi/idle.png b/Resources/Textures/Buildings/gravity_generator_core.rsi/idle.png new file mode 100644 index 0000000000..14b5452ebf Binary files /dev/null and b/Resources/Textures/Buildings/gravity_generator_core.rsi/idle.png differ diff --git a/Resources/Textures/Buildings/gravity_generator_core.rsi/meta.json b/Resources/Textures/Buildings/gravity_generator_core.rsi/meta.json new file mode 100644 index 0000000000..db482cdb7d --- /dev/null +++ b/Resources/Textures/Buildings/gravity_generator_core.rsi/meta.json @@ -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] + ] + } + ] +} diff --git a/Resources/Textures/Buildings/gravity_generator_core.rsi/startup.png b/Resources/Textures/Buildings/gravity_generator_core.rsi/startup.png new file mode 100644 index 0000000000..b5a5206b66 Binary files /dev/null and b/Resources/Textures/Buildings/gravity_generator_core.rsi/startup.png differ diff --git a/Resources/Textures/Objects/Tools/autolathe_welder.png b/Resources/Textures/Objects/Tools/autolathe_welder.png deleted file mode 100644 index 6b1ed2cf13..0000000000 Binary files a/Resources/Textures/Objects/Tools/autolathe_welder.png and /dev/null differ diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index 504ec81af9..05cf30d140 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -1,9 +1,12 @@ version: 1 # Not used right now, whatever. binds: -- function: UseOrAttack +- function: Use type: state key: MouseLeft canFocus: true +- function: WideAttack + type: state + key: Space - function: ShowDebugMonitors type: Toggle key: F3 @@ -65,6 +68,14 @@ binds: - function: OpenCharacterMenu type: State key: C +- function: TextCursorSelect + # TextCursorSelect HAS to be above ExamineEntity + # So that LineEdit receives it correctly. + # TODO: Make it so that UI keybinds are somehow prioritized so this ordering stuff isn't necessary. + type: state + key: MouseLeft + mod1: Shift + canFocus: true - function: ExamineEntity type: State key: MouseLeft @@ -94,6 +105,14 @@ binds: - function: OpenInventoryMenu type: state key: I +- function: SmartEquipBackpack + type: State + key: B + mod1: Shift +- function: SmartEquipBelt + type: State + key: E + mod1: Shift - function: ShowDebugConsole type: state key: Tilde @@ -109,6 +128,16 @@ binds: type: state key: Right canRepeat: true +- function: TextCursorWordLeft + type: state + key: Left + mod1: Control + canRepeat: true +- function: TextCursorWordRight + type: state + key: Right + mod1: Control + canRepeat: true - function: TextCursorBegin type: state key: Home @@ -116,6 +145,37 @@ binds: type: state key: End canRepeat: true +- function: TextCursorSelectLeft + type: state + key: Left + mod1: Shift + canRepeat: true +- function: TextCursorSelectRight + type: state + key: Right + mod1: Shift + canRepeat: true +- function: TextCursorSelectWordLeft + type: state + key: Left + mod1: Shift + mod2: Control + canRepeat: true +- function: TextCursorSelectWordRight + type: state + key: Right + mod1: Shift + mod2: Control + canRepeat: true +- function: TextCursorSelectBegin + type: state + mod1: Shift + key: Home +- function: TextCursorSelectEnd + type: state + mod1: Shift + key: End + canRepeat: true - function: TextBackspace type: state key: BackSpace @@ -126,6 +186,18 @@ binds: - function: TextSubmit type: state key: NumpadEnter +- function: TextSelectAll + type: state + key: A + mod1: Control +- function: TextCopy + type: state + key: C + mod1: Control +- function: TextCut + type: state + key: X + mod1: Control - function: TextPaste type: state key: V @@ -155,3 +227,10 @@ binds: - function: OpenSandboxWindow type: state key: B +- function: TakeScreenshot + type: state + key: F2 +- function: TakeScreenshotNoUI + type: state + key: F2 + mod1: Shift diff --git a/RobustToolbox b/RobustToolbox index 7c4b1af967..51b87513ed 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 7c4b1af96743d2b87a787726c851e141b7ba45c2 +Subproject commit 51b87513ed7867b97a0c5d3bb4146a9e40a5da1d diff --git a/Setup.bat b/Setup.bat deleted file mode 100644 index 1da212630f..0000000000 --- a/Setup.bat +++ /dev/null @@ -1,6 +0,0 @@ -cd %CD% -python RUN_THIS.py -pause -cd %CD%\engine\Resources -python buildResourcePack.py -pause \ No newline at end of file diff --git a/SpaceStation14.sln b/SpaceStation14.sln index df58010a94..ccb629b7e1 100644 --- a/SpaceStation14.sln +++ b/SpaceStation14.sln @@ -62,6 +62,13 @@ ProjectSection(SolutionItems) = preProject Tools\gen_build_info.py = Tools\gen_build_info.py EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Shared.Scripting", "RobustToolbox\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj", "{41B450C0-A361-4CD7-8121-7072B8995CFC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{DDD55F86-0406-42E5-8443-93EDC6BB2D75}" +ProjectSection(SolutionItems) = preProject + RobustToolbox\Tools\download_natives.py = RobustToolbox\Tools\download_natives.py +EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -128,6 +135,10 @@ Global {45C9B43F-305D-4651-9863-F6384CBC847F}.Debug|x64.Build.0 = Debug|x64 {45C9B43F-305D-4651-9863-F6384CBC847F}.Release|x64.ActiveCfg = Release|x64 {45C9B43F-305D-4651-9863-F6384CBC847F}.Release|x64.Build.0 = Release|x64 + {41B450C0-A361-4CD7-8121-7072B8995CFC}.Debug|x64.ActiveCfg = Debug|Any CPU + {41B450C0-A361-4CD7-8121-7072B8995CFC}.Debug|x64.Build.0 = Debug|Any CPU + {41B450C0-A361-4CD7-8121-7072B8995CFC}.Release|x64.ActiveCfg = Release|Any CPU + {41B450C0-A361-4CD7-8121-7072B8995CFC}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -142,6 +153,8 @@ Global {0529F740-0000-0000-0000-000000000000} = {83B4CBBA-547A-42F0-A7CD-8A67D93196CE} {4809F412-3132-419E-BF9D-CCF7593C3533} = {83B4CBBA-547A-42F0-A7CD-8A67D93196CE} {50404922-9637-4394-BF59-165D0850ADC8} = {83B4CBBA-547A-42F0-A7CD-8A67D93196CE} + {41B450C0-A361-4CD7-8121-7072B8995CFC} = {83B4CBBA-547A-42F0-A7CD-8A67D93196CE} + {DDD55F86-0406-42E5-8443-93EDC6BB2D75} = {83B4CBBA-547A-42F0-A7CD-8A67D93196CE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AA37ED9F-F8D6-468E-A101-658AD605B09A} diff --git a/Tools/gen_build_info.py b/Tools/gen_build_info.py index ca1440f8af..b988609d67 100755 --- a/Tools/gen_build_info.py +++ b/Tools/gen_build_info.py @@ -15,6 +15,7 @@ FILES = { SERVER_FILES = [ "SS14.Server_Linux_x64.zip", + "SS14.Server_Linux_ARM64.zip", "SS14.Server_Windows_x64.zip", "SS14.Server_macOS_x64.zip" ] diff --git a/Tools/package_release_build.py b/Tools/package_release_build.py index d127c67de0..007eca89f1 100755 --- a/Tools/package_release_build.py +++ b/Tools/package_release_build.py @@ -28,6 +28,7 @@ p = os.path.join PLATFORM_WINDOWS = "windows" PLATFORM_LINUX = "linux" +PLATFORM_LINUX_ARM64 = "linux-arm64" PLATFORM_MACOS = "mac" SHARED_IGNORED_RESOURCES = { @@ -62,7 +63,14 @@ WINDOWS_NATIVES = { "freetype6.dll", "openal32.dll", "swnfd.dll", - "glfw3.dll" + "glfw3.dll", + "fluidsynth.dll", + "libglib-2.0-0.dll", + "libgobject-2.0-0.dll", + "libgthread-2.0-0.dll", + "libinstpatch-2.dll", + "libintl-8.dll", + "libsndfile-1.dll" } LINUX_NATIVES = { @@ -85,7 +93,7 @@ def main() -> None: parser.add_argument("--platform", "-p", action="store", - choices=[PLATFORM_WINDOWS, PLATFORM_MACOS, PLATFORM_LINUX], + choices=[PLATFORM_WINDOWS, PLATFORM_MACOS, PLATFORM_LINUX, PLATFORM_LINUX_ARM64], nargs="*", help="Which platform to build for. If not provided, all platforms will be built") @@ -117,6 +125,11 @@ def main() -> None: wipe_bin() build_linux(skip_build) + if PLATFORM_LINUX_ARM64 in platforms: + if not skip_build: + wipe_bin() + build_linux_arm64(skip_build) + if PLATFORM_MACOS in platforms: if not skip_build: wipe_bin() @@ -253,7 +266,36 @@ def build_linux(skip_build: bool) -> None: copy_content_assemblies(p("Resources", "Assemblies"), server_zip, server=True) server_zip.close() -def publish_client_server(runtime: str, target_os: str) -> None: + +def build_linux_arm64(skip_build: bool) -> None: + # Run a full build. + print(Fore.GREEN + "Building project for Linux ARM64 (SERVER ONLY)..." + Style.RESET_ALL) + + if not skip_build: + subprocess.run([ + "dotnet", + "build", + "SpaceStation14.sln", + "-c", "Release", + "--nologo", + "/v:m", + "/p:TargetOS=Linux", + "/t:Rebuild", + "/p:FullRelease=True" + ], check=True) + + publish_client_server("linux-arm64", "Linux", True) + + print(Fore.GREEN + "Packaging Linux ARM64 server..." + Style.RESET_ALL) + server_zip = zipfile.ZipFile(p("release", "SS14.Server_Linux_ARM64.zip"), "w", + compression=zipfile.ZIP_DEFLATED) + copy_dir_into_zip(p("RobustToolbox", "bin", "Server", "linux-arm64", "publish"), "", server_zip) + copy_resources(p("Resources"), server_zip, server=True) + copy_content_assemblies(p("Resources", "Assemblies"), server_zip, server=True) + server_zip.close() + + +def publish_client_server(runtime: str, target_os: str, actually_only_server: bool = False) -> None: # Runs dotnet publish on client and server. base = [ "dotnet", "publish", @@ -264,9 +306,12 @@ def publish_client_server(runtime: str, target_os: str) -> None: "/p:FullRelease=True", ] - subprocess.run(base + ["RobustToolbox/Robust.Client/Robust.Client.csproj"], check=True) + if not actually_only_server: + subprocess.run(base + ["RobustToolbox/Robust.Client/Robust.Client.csproj"], check=True) + subprocess.run(base + ["RobustToolbox/Robust.Server/Robust.Server.csproj"], check=True) + def copy_resources(target, zipf, server): # Content repo goes FIRST so that it won't override engine files as that's forbidden. ignore_set = SHARED_IGNORED_RESOURCES @@ -368,10 +413,12 @@ def copy_dir_or_file(src: str, dst: str): else: raise IOError("{} is neither file nor directory. Can't copy.".format(src)) + def copy_client_natives(fileNames: List[str], zipf: zipfile.ZipFile, zipPath: str): for fileName in fileNames: zipf.write(p("RobustToolbox", "bin", "Client", fileName), p(zipPath, fileName)) print(f"writing native {fileName}") + if __name__ == '__main__': main()