diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4e72256cb8..16cb5017d6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,31 +41,22 @@ jobs: - name: Package client run: dotnet run --project Content.Packaging client --no-wipe-release - - name: Update Build Info - run: Tools/gen_build_info.py - - - name: Shuffle files around - run: | - mkdir "release/${{ github.sha }}" - mv release/*.zip "release/${{ github.sha }}" - - - name: Upload files to centcomm - uses: appleboy/scp-action@master + - name: Upload build artifact + id: artifact-upload-step + uses: actions/upload-artifact@v4 with: - host: centcomm.spacestation14.io - username: wizards-build-push - key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }} - source: "release/${{ github.sha }}" - target: "/home/wizards-build-push/builds_dir/builds/" - strip_components: 1 + name: build + path: release/*.zip + compression-level: 0 + retention-days: 0 - - name: Update manifest JSON - uses: appleboy/ssh-action@master - with: - host: centcomm.spacestation14.io - username: wizards-build-push - key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }} - script: /home/wizards-build-push/push.ps1 ${{ github.sha }} + - name: Publish version + run: Tools/publish_github_artifact.py + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + ARTIFACT_ID: ${{ steps.artifact-upload-step.outputs.artifact-id }} + GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }} - name: Publish changelog (Discord) run: Tools/actions_changelogs_since_last_run.py @@ -77,3 +68,8 @@ jobs: run: Tools/actions_changelog_rss.py env: CHANGELOG_RSS_KEY: ${{ secrets.CHANGELOG_RSS_KEY }} + + - uses: geekyeggo/delete-artifact@v5 + if: always() + with: + name: build diff --git a/.github/workflows/test-packaging.yml b/.github/workflows/test-packaging.yml index 2dce502697..2d002f869a 100644 --- a/.github/workflows/test-packaging.yml +++ b/.github/workflows/test-packaging.yml @@ -64,11 +64,3 @@ jobs: - name: Package client run: dotnet run --project Content.Packaging client --no-wipe-release - - - name: Update Build Info - run: Tools/gen_build_info.py - - - name: Shuffle files around - run: | - mkdir "release/${{ github.sha }}" - mv release/*.zip "release/${{ github.sha }}" diff --git a/Content.Client/Access/UI/AccessLevelControl.xaml.cs b/Content.Client/Access/UI/AccessLevelControl.xaml.cs index 34db80b7af..12487b2e5c 100644 --- a/Content.Client/Access/UI/AccessLevelControl.xaml.cs +++ b/Content.Client/Access/UI/AccessLevelControl.xaml.cs @@ -12,11 +12,18 @@ namespace Content.Client.Access.UI; [GenerateTypedNameReferences] public sealed partial class AccessLevelControl : GridContainer { + [Dependency] private readonly ILogManager _logManager = default!; + + private ISawmill _sawmill = default!; + public readonly Dictionary, Button> ButtonsList = new(); public AccessLevelControl() { RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + _sawmill = _logManager.GetSawmill("accesslevelcontrol"); } public void Populate(List> accessLevels, IPrototypeManager prototypeManager) @@ -25,7 +32,7 @@ public sealed partial class AccessLevelControl : GridContainer { if (!prototypeManager.TryIndex(access, out var accessLevel)) { - Logger.Error($"Unable to find accesslevel for {access}"); + _sawmill.Error($"Unable to find accesslevel for {access}"); continue; } diff --git a/Content.Client/Actions/UI/ActionAlertTooltip.cs b/Content.Client/Actions/UI/ActionAlertTooltip.cs index ddc498b6e9..f805f6643d 100644 --- a/Content.Client/Actions/UI/ActionAlertTooltip.cs +++ b/Content.Client/Actions/UI/ActionAlertTooltip.cs @@ -1,4 +1,4 @@ -using Content.Client.Stylesheets; +using Content.Client.Stylesheets; using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -77,9 +77,12 @@ namespace Content.Client.Actions.UI MaxWidth = TooltipTextMaxWidth, StyleClasses = {StyleNano.StyleClassTooltipActionRequirements} }; - requiresLabel.SetMessage(FormattedMessage.FromMarkup("[color=#635c5c]" + - requires + - "[/color]")); + + if (!FormattedMessage.TryFromMarkup("[color=#635c5c]" + requires + "[/color]", out var markup)) + return; + + requiresLabel.SetMessage(markup); + vbox.AddChild(requiresLabel); } } @@ -97,8 +100,11 @@ namespace Content.Client.Actions.UI if (timeLeft > TimeSpan.Zero) { var duration = Cooldown.Value.End - Cooldown.Value.Start; - _cooldownLabel.SetMessage(FormattedMessage.FromMarkup( - $"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]")); + + if (!FormattedMessage.TryFromMarkup($"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]", out var markup)) + return; + + _cooldownLabel.SetMessage(markup); _cooldownLabel.Visible = true; } else diff --git a/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs b/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs index 84b787a4ec..d60c978ccf 100644 --- a/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs +++ b/Content.Client/Audio/ContentAudioSystem.AmbientMusic.cs @@ -4,6 +4,7 @@ using Content.Shared.Audio; using Content.Shared.CCVar; using Content.Shared.GameTicking; using Content.Shared.Random; +using Content.Shared.Random.Rules; using Robust.Client.GameObjects; using Robust.Client.Player; using Robust.Client.ResourceManagement; diff --git a/Content.Client/Clock/ClockSystem.cs b/Content.Client/Clock/ClockSystem.cs new file mode 100644 index 0000000000..7097ada9df --- /dev/null +++ b/Content.Client/Clock/ClockSystem.cs @@ -0,0 +1,26 @@ +using Content.Shared.Clock; +using Robust.Client.GameObjects; + +namespace Content.Client.Clock; + +public sealed class ClockSystem : SharedClockSystem +{ + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var sprite)) + { + if (!sprite.LayerMapTryGet(ClockVisualLayers.HourHand, out var hourLayer) || + !sprite.LayerMapTryGet(ClockVisualLayers.MinuteHand, out var minuteLayer)) + continue; + + var time = GetClockTime((uid, comp)); + var hourState = $"{comp.HoursBase}{time.Hours % 12}"; + var minuteState = $"{comp.MinutesBase}{time.Minutes / 5}"; + sprite.LayerSetState(hourLayer, hourState); + sprite.LayerSetState(minuteLayer, minuteState); + } + } +} diff --git a/Content.Client/Construction/ConstructionSystem.cs b/Content.Client/Construction/ConstructionSystem.cs index 453658bebf..889c992f7f 100644 --- a/Content.Client/Construction/ConstructionSystem.cs +++ b/Content.Client/Construction/ConstructionSystem.cs @@ -48,11 +48,11 @@ namespace Content.Client.Construction CommandBinds.Builder .Bind(ContentKeyFunctions.OpenCraftingMenu, - new PointerInputCmdHandler(HandleOpenCraftingMenu, outsidePrediction:true)) + new PointerInputCmdHandler(HandleOpenCraftingMenu, outsidePrediction: true)) .Bind(EngineKeyFunctions.Use, new PointerInputCmdHandler(HandleUse, outsidePrediction: true)) .Bind(ContentKeyFunctions.EditorFlipObject, - new PointerInputCmdHandler(HandleFlip, outsidePrediction:true)) + new PointerInputCmdHandler(HandleFlip, outsidePrediction: true)) .Register(); SubscribeLocalEvent(HandleConstructionGhostExamined); @@ -196,7 +196,7 @@ namespace Content.Client.Construction if (GhostPresent(loc)) return false; - var predicate = GetPredicate(prototype.CanBuildInImpassable, loc.ToMap(EntityManager, _transformSystem)); + var predicate = GetPredicate(prototype.CanBuildInImpassable, _transformSystem.ToMapCoordinates(loc)); if (!_examineSystem.InRangeUnOccluded(user, loc, 20f, predicate: predicate)) return false; diff --git a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs index a60619baa3..0462c095ba 100644 --- a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs +++ b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs @@ -170,7 +170,7 @@ namespace Content.Client.ContextMenu.UI if (_combatMode.IsInCombatMode(args.Session?.AttachedEntity)) return false; - var coords = args.Coordinates.ToMap(_entityManager, _xform); + var coords = _xform.ToMapCoordinates(args.Coordinates); if (_verbSystem.TryGetEntityMenuEntities(coords, out var entities)) OpenRootMenu(entities); diff --git a/Content.Client/Extinguisher/FireExtinguisherComponent.cs b/Content.Client/Extinguisher/FireExtinguisherComponent.cs deleted file mode 100644 index 324b05a93d..0000000000 --- a/Content.Client/Extinguisher/FireExtinguisherComponent.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Content.Shared.Extinguisher; -using Robust.Shared.GameStates; - -namespace Content.Client.Extinguisher; - -[RegisterComponent] -public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent; diff --git a/Content.Client/Flash/FlashOverlay.cs b/Content.Client/Flash/FlashOverlay.cs index 9ea00275e8..046be2aa62 100644 --- a/Content.Client/Flash/FlashOverlay.cs +++ b/Content.Client/Flash/FlashOverlay.cs @@ -1,27 +1,22 @@ using Content.Shared.Flash; using Content.Shared.Flash.Components; using Content.Shared.StatusEffect; -using Content.Client.Viewport; using Robust.Client.Graphics; -using Robust.Client.State; using Robust.Client.Player; using Robust.Shared.Enums; using Robust.Shared.Prototypes; using Robust.Shared.Timing; -using SixLabors.ImageSharp.PixelFormats; namespace Content.Client.Flash { public sealed class FlashOverlay : Overlay { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IClyde _displayManager = default!; - [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IGameTiming _timing = default!; - private readonly StatusEffectsSystem _statusSys; + private readonly StatusEffectsSystem _statusSys; public override OverlaySpace Space => OverlaySpace.WorldSpace; private readonly ShaderInstance _shader; @@ -56,20 +51,6 @@ namespace Content.Client.Flash PercentComplete = timeDone / lastsFor; } - public void ReceiveFlash() - { - if (_stateManager.CurrentState is IMainViewportState state) - { - // take a screenshot - // note that the callback takes a while and ScreenshotTexture will be null the first few Draws - state.Viewport.Viewport.Screenshot(image => - { - var rgba32Image = image.CloneAs(SixLabors.ImageSharp.Configuration.Default); - ScreenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image); - }); - } - } - protected override bool BeforeDraw(in OverlayDrawArgs args) { if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp)) @@ -82,6 +63,11 @@ namespace Content.Client.Flash protected override void Draw(in OverlayDrawArgs args) { + if (RequestScreenTexture && ScreenTexture != null) + { + ScreenshotTexture = ScreenTexture; + RequestScreenTexture = false; // we only need the first frame, so we can stop the request now for performance reasons + } if (ScreenshotTexture == null) return; @@ -96,7 +82,6 @@ namespace Content.Client.Flash { base.DisposeBehavior(); ScreenshotTexture = null; - PercentComplete = 1.0f; } } } diff --git a/Content.Client/Flash/FlashSystem.cs b/Content.Client/Flash/FlashSystem.cs index 9a0579f6aa..146d84b990 100644 --- a/Content.Client/Flash/FlashSystem.cs +++ b/Content.Client/Flash/FlashSystem.cs @@ -22,7 +22,6 @@ public sealed class FlashSystem : SharedFlashSystem SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); - SubscribeLocalEvent(OnStatusAdded); _overlay = new(); } @@ -34,8 +33,8 @@ public sealed class FlashSystem : SharedFlashSystem private void OnPlayerDetached(EntityUid uid, FlashedComponent component, LocalPlayerDetachedEvent args) { - _overlay.PercentComplete = 1.0f; _overlay.ScreenshotTexture = null; + _overlay.RequestScreenTexture = false; _overlayMan.RemoveOverlay(_overlay); } @@ -43,6 +42,7 @@ public sealed class FlashSystem : SharedFlashSystem { if (_player.LocalEntity == uid) { + _overlay.RequestScreenTexture = true; _overlayMan.AddOverlay(_overlay); } } @@ -51,17 +51,9 @@ public sealed class FlashSystem : SharedFlashSystem { if (_player.LocalEntity == uid) { - _overlay.PercentComplete = 1.0f; _overlay.ScreenshotTexture = null; + _overlay.RequestScreenTexture = false; _overlayMan.RemoveOverlay(_overlay); } } - - private void OnStatusAdded(EntityUid uid, FlashedComponent component, StatusEffectAddedEvent args) - { - if (_player.LocalEntity == uid && args.Key == FlashedKey) - { - _overlay.ReceiveFlash(); - } - } } diff --git a/Content.Client/Gameplay/GameplayStateBase.cs b/Content.Client/Gameplay/GameplayStateBase.cs index 6236cd8e95..63cbfdb09c 100644 --- a/Content.Client/Gameplay/GameplayStateBase.cs +++ b/Content.Client/Gameplay/GameplayStateBase.cs @@ -104,7 +104,8 @@ namespace Content.Client.Gameplay public IEnumerable GetClickableEntities(EntityCoordinates coordinates) { - return GetClickableEntities(coordinates.ToMap(_entityManager, _entitySystemManager.GetEntitySystem())); + var transformSystem = _entitySystemManager.GetEntitySystem(); + return GetClickableEntities(transformSystem.ToMapCoordinates(coordinates)); } public IEnumerable GetClickableEntities(MapCoordinates coordinates) diff --git a/Content.Client/MainMenu/MainMenu.cs b/Content.Client/MainMenu/MainMenu.cs index 43c5bfe567..3c709d2d15 100644 --- a/Content.Client/MainMenu/MainMenu.cs +++ b/Content.Client/MainMenu/MainMenu.cs @@ -25,6 +25,9 @@ namespace Content.Client.MainMenu [Dependency] private readonly IGameController _controllerProxy = default!; [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; + [Dependency] private readonly ILogManager _logManager = default!; + + private ISawmill _sawmill = default!; private MainMenuControl _mainMenuControl = default!; private bool _isConnecting; @@ -35,6 +38,8 @@ namespace Content.Client.MainMenu /// protected override void Startup() { + _sawmill = _logManager.GetSawmill("mainmenu"); + _mainMenuControl = new MainMenuControl(_resourceCache, _configurationManager); _userInterfaceManager.StateRoot.AddChild(_mainMenuControl); @@ -116,7 +121,7 @@ namespace Content.Client.MainMenu catch (ArgumentException e) { _userInterfaceManager.Popup($"Unable to connect: {e.Message}", "Connection error."); - Logger.Warning(e.ToString()); + _sawmill.Warning(e.ToString()); _netManager.ConnectFailed -= _onConnectFailed; _setConnectingState(false); } diff --git a/Content.Client/Materials/MaterialStorageSystem.cs b/Content.Client/Materials/MaterialStorageSystem.cs index edd07391f7..592471d673 100644 --- a/Content.Client/Materials/MaterialStorageSystem.cs +++ b/Content.Client/Materials/MaterialStorageSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Materials; +using Content.Shared.Materials; using Robust.Client.GameObjects; namespace Content.Client.Materials; @@ -49,7 +49,7 @@ public sealed class MaterialStorageSystem : SharedMaterialStorageSystem { if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, material, composition)) return false; - _transform.DetachParentToNull(toInsert, Transform(toInsert)); + _transform.DetachEntity(toInsert, Transform(toInsert)); return true; } } diff --git a/Content.Client/Movement/Systems/JetpackSystem.cs b/Content.Client/Movement/Systems/JetpackSystem.cs index b7f5e48821..e25300d44c 100644 --- a/Content.Client/Movement/Systems/JetpackSystem.cs +++ b/Content.Client/Movement/Systems/JetpackSystem.cs @@ -15,6 +15,7 @@ public sealed class JetpackSystem : SharedJetpackSystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ClothingSystem _clothing = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; public override void Initialize() { @@ -73,11 +74,11 @@ public sealed class JetpackSystem : SharedJetpackSystem var uidXform = Transform(uid); var coordinates = uidXform.Coordinates; - var gridUid = coordinates.GetGridUid(EntityManager); + var gridUid = _transform.GetGrid(coordinates); if (TryComp(gridUid, out var grid)) { - coordinates = new EntityCoordinates(gridUid.Value, grid.WorldToLocal(coordinates.ToMapPos(EntityManager, _transform))); + coordinates = new EntityCoordinates(gridUid.Value, _mapSystem.WorldToLocal(gridUid.Value, grid, _transform.ToMapCoordinates(coordinates).Position)); } else if (uidXform.MapUid != null) { diff --git a/Content.Client/NPC/PathfindingSystem.cs b/Content.Client/NPC/PathfindingSystem.cs index d3ae509152..0c72a8f99f 100644 --- a/Content.Client/NPC/PathfindingSystem.cs +++ b/Content.Client/NPC/PathfindingSystem.cs @@ -203,7 +203,7 @@ namespace Content.Client.NPC if (found || !_system.Breadcrumbs.TryGetValue(netGrid, out var crumbs) || !xformQuery.TryGetComponent(grid, out var gridXform)) continue; - var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); + var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform); var localAABB = invWorldMatrix.TransformBox(aabb.Enlarged(float.Epsilon - SharedPathfindingSystem.ChunkSize)); foreach (var chunk in crumbs) @@ -287,7 +287,7 @@ namespace Content.Client.NPC return; } - var invGridMatrix = gridXform.InvWorldMatrix; + var invGridMatrix = _transformSystem.GetInvWorldMatrix(gridXform); DebugPathPoly? nearest = null; foreach (var poly in tile) @@ -359,7 +359,7 @@ namespace Content.Client.NPC continue; } - var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); + var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform); worldHandle.SetTransform(worldMatrix); var localAABB = invWorldMatrix.TransformBox(aabb); @@ -419,7 +419,7 @@ namespace Content.Client.NPC !xformQuery.TryGetComponent(grid, out var gridXform)) continue; - var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); + var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform); worldHandle.SetTransform(worldMatrix); var localAABB = invWorldMatrix.TransformBox(aabb); @@ -458,7 +458,7 @@ namespace Content.Client.NPC !xformQuery.TryGetComponent(grid, out var gridXform)) continue; - var (_, _, worldMatrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); + var (_, _, worldMatrix, invMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform); worldHandle.SetTransform(worldMatrix); var localAABB = invMatrix.TransformBox(aabb); @@ -483,7 +483,7 @@ namespace Content.Client.NPC if (neighborPoly.NetEntity != poly.GraphUid) { color = Color.Green; - var neighborMap = _entManager.GetCoordinates(neighborPoly).ToMap(_entManager, _transformSystem); + var neighborMap = _transformSystem.ToMapCoordinates(_entManager.GetCoordinates(neighborPoly)); if (neighborMap.MapId != args.MapId) continue; @@ -517,7 +517,7 @@ namespace Content.Client.NPC !xformQuery.TryGetComponent(grid, out var gridXform)) continue; - var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); + var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform); worldHandle.SetTransform(worldMatrix); var localAABB = invWorldMatrix.TransformBox(args.WorldBounds); @@ -544,7 +544,7 @@ namespace Content.Client.NPC if (!_entManager.TryGetComponent(_entManager.GetEntity(node.GraphUid), out var graphXform)) continue; - worldHandle.SetTransform(graphXform.WorldMatrix); + worldHandle.SetTransform(_transformSystem.GetWorldMatrix(graphXform)); worldHandle.DrawRect(node.Box, Color.Orange.WithAlpha(0.10f)); } } @@ -568,7 +568,7 @@ namespace Content.Client.NPC continue; matrix = graph; - worldHandle.SetTransform(graphXform.WorldMatrix); + worldHandle.SetTransform(_transformSystem.GetWorldMatrix(graphXform)); } worldHandle.DrawRect(node.Box, new Color(0f, cost / highestGScore, 1f - (cost / highestGScore), 0.10f)); diff --git a/Content.Client/Physics/JointVisualsOverlay.cs b/Content.Client/Physics/JointVisualsOverlay.cs index 09c02746e2..e0b3499a97 100644 --- a/Content.Client/Physics/JointVisualsOverlay.cs +++ b/Content.Client/Physics/JointVisualsOverlay.cs @@ -58,8 +58,8 @@ public sealed class JointVisualsOverlay : Overlay coordsA = coordsA.Offset(rotA.RotateVec(visuals.OffsetA)); coordsB = coordsB.Offset(rotB.RotateVec(visuals.OffsetB)); - var posA = coordsA.ToMapPos(_entManager, xformSystem); - var posB = coordsB.ToMapPos(_entManager, xformSystem); + var posA = xformSystem.ToMapCoordinates(coordsA).Position; + var posB = xformSystem.ToMapCoordinates(coordsB).Position; var diff = (posB - posA); var length = diff.Length(); diff --git a/Content.Client/Pinpointer/UI/NavMapControl.cs b/Content.Client/Pinpointer/UI/NavMapControl.cs index 3c99a18818..413b41c36a 100644 --- a/Content.Client/Pinpointer/UI/NavMapControl.cs +++ b/Content.Client/Pinpointer/UI/NavMapControl.cs @@ -228,8 +228,8 @@ public partial class NavMapControl : MapGridControl { if (!blip.Selectable) continue; - - var currentDistance = (blip.Coordinates.ToMapPos(EntManager, _transformSystem) - worldPosition).Length(); + + var currentDistance = (_transformSystem.ToMapCoordinates(blip.Coordinates).Position - worldPosition).Length(); if (closestDistance < currentDistance || currentDistance * MinimapScale > MaxSelectableDistance) continue; @@ -397,7 +397,7 @@ public partial class NavMapControl : MapGridControl { if (lit && value.Visible) { - var mapPos = coord.ToMap(EntManager, _transformSystem); + var mapPos = _transformSystem.ToMapCoordinates(coord); if (mapPos.MapId != MapId.Nullspace) { @@ -418,7 +418,7 @@ public partial class NavMapControl : MapGridControl if (blip.Texture == null) continue; - var mapPos = blip.Coordinates.ToMap(EntManager, _transformSystem); + var mapPos = _transformSystem.ToMapCoordinates(blip.Coordinates); if (mapPos.MapId != MapId.Nullspace) { @@ -535,7 +535,7 @@ public partial class NavMapControl : MapGridControl // East edge neighborData = 0; if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1) - neighborData = chunk.TileData[i+SharedNavMapSystem.ChunkSize]; + neighborData = chunk.TileData[i + SharedNavMapSystem.ChunkSize]; else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk)) neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize]; @@ -548,7 +548,7 @@ public partial class NavMapControl : MapGridControl // South edge neighborData = 0; if (relativeTile.Y != 0) - neighborData = chunk.TileData[i-1]; + neighborData = chunk.TileData[i - 1]; else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk)) neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize]; @@ -561,7 +561,7 @@ public partial class NavMapControl : MapGridControl // West edge neighborData = 0; if (relativeTile.X != 0) - neighborData = chunk.TileData[i-SharedNavMapSystem.ChunkSize]; + neighborData = chunk.TileData[i - SharedNavMapSystem.ChunkSize]; else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk)) neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize]; diff --git a/Content.Client/RCD/AlignRCDConstruction.cs b/Content.Client/RCD/AlignRCDConstruction.cs index d5425425a7..ef99b01855 100644 --- a/Content.Client/RCD/AlignRCDConstruction.cs +++ b/Content.Client/RCD/AlignRCDConstruction.cs @@ -45,7 +45,7 @@ public sealed class AlignRCDConstruction : PlacementMode _unalignedMouseCoords = ScreenToCursorGrid(mouseScreen); MouseCoords = _unalignedMouseCoords.AlignWithClosestGridTile(SearchBoxSize, _entityManager, _mapManager); - var gridId = MouseCoords.GetGridUid(_entityManager); + var gridId = _transformSystem.GetGrid(MouseCoords); if (!_entityManager.TryGetComponent(gridId, out var mapGrid)) return; @@ -75,7 +75,7 @@ public sealed class AlignRCDConstruction : PlacementMode if (!_entityManager.TryGetComponent(player, out var xform)) return false; - if (!xform.Coordinates.InRange(_entityManager, _transformSystem, position, SharedInteractionSystem.InteractionRange)) + if (!_transformSystem.InRange(xform.Coordinates, position, SharedInteractionSystem.InteractionRange)) { InvalidPlaceColor = InvalidPlaceColor.WithAlpha(0); return false; @@ -105,8 +105,8 @@ public sealed class AlignRCDConstruction : PlacementMode if (currentState is not GameplayStateBase screen) return false; - - var target = screen.GetClickedEntity(_unalignedMouseCoords.ToMap(_entityManager, _transformSystem)); + + var target = screen.GetClickedEntity(_transformSystem.ToMapCoordinates(_unalignedMouseCoords)); // Determine if the RCD operation is valid or not if (!_rcdSystem.IsRCDOperationStillValid(heldEntity.Value, rcd, mapGridData.Value, target, player.Value, false)) diff --git a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs index 8d5607af2d..9ec24fae0e 100644 --- a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs +++ b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs @@ -116,7 +116,9 @@ namespace Content.Client.Radiation.Overlays var shaderInstance = _pulses[pulseEntity]; shaderInstance.instance.CurrentMapCoords = _transform.GetMapCoordinates(pulseEntity); shaderInstance.instance.Range = pulse.VisualRange; - } else { + } + else + { _pulses[pulseEntity].shd.Dispose(); _pulses.Remove(pulseEntity); } @@ -129,7 +131,7 @@ namespace Content.Client.Radiation.Overlays var transformComponent = _entityManager.GetComponent(pulseEntity); var transformSystem = _entityManager.System(); return transformComponent.MapID == currentEyeLoc.MapId - && transformComponent.Coordinates.InRange(_entityManager, transformSystem, EntityCoordinates.FromMap(transformComponent.ParentUid, currentEyeLoc, transformSystem, _entityManager), MaxDist); + && transformSystem.InRange(transformComponent.Coordinates, transformSystem.ToCoordinates(transformComponent.ParentUid, currentEyeLoc), MaxDist); } private sealed record RadiationShaderInstance(MapCoordinates CurrentMapCoords, float Range, TimeSpan Start, float Duration) diff --git a/Content.Client/Sandbox/SandboxSystem.cs b/Content.Client/Sandbox/SandboxSystem.cs index 6a1129bb75..8a4c93fa35 100644 --- a/Content.Client/Sandbox/SandboxSystem.cs +++ b/Content.Client/Sandbox/SandboxSystem.cs @@ -17,6 +17,7 @@ namespace Content.Client.Sandbox [Dependency] private readonly IPlacementManager _placement = default!; [Dependency] private readonly ContentEyeSystem _contentEye = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; private bool _sandboxEnabled; public bool SandboxAllowed { get; private set; } @@ -92,7 +93,7 @@ namespace Content.Client.Sandbox && EntityManager.TryGetComponent(uid, out MetaDataComponent? comp) && !comp.EntityDeleted) { - if (comp.EntityPrototype == null || comp.EntityPrototype.NoSpawn || comp.EntityPrototype.Abstract) + if (comp.EntityPrototype == null || comp.EntityPrototype.HideSpawnMenu || comp.EntityPrototype.Abstract) return false; if (_placement.Eraser) @@ -109,7 +110,8 @@ namespace Content.Client.Sandbox } // Try copy tile. - if (!_map.TryFindGridAt(coords.ToMap(EntityManager, _transform), out _, out var grid) || !grid.TryGetTileRef(coords, out var tileRef)) + + if (!_map.TryFindGridAt(_transform.ToMapCoordinates(coords), out var gridUid, out var grid) || !_mapSystem.TryGetTileRef(gridUid, grid, coords, out var tileRef)) return false; if (_placement.Eraser) diff --git a/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs b/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs index c134b7157c..eb9ec285f8 100644 --- a/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs +++ b/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs @@ -35,9 +35,9 @@ public sealed partial class ShuttleSystem switch (mapObj) { case ShuttleBeaconObject beacon: - return GetCoordinates(beacon.Coordinates).ToMap(EntityManager, XformSystem); + return XformSystem.ToMapCoordinates(GetCoordinates(beacon.Coordinates)); case ShuttleExclusionObject exclusion: - return GetCoordinates(exclusion.Coordinates).ToMap(EntityManager, XformSystem); + return XformSystem.ToMapCoordinates(GetCoordinates(exclusion.Coordinates)); case GridMapObject grid: var gridXform = Transform(grid.Entity); diff --git a/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs index 31f0eecad7..61ae069926 100644 --- a/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs +++ b/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs @@ -107,7 +107,7 @@ public sealed partial class ShuttleDockControl : BaseShuttleControl DrawCircles(handle); var gridNent = EntManager.GetNetEntity(GridEntity); var mapPos = _xformSystem.ToMapCoordinates(_coordinates.Value); - var ourGridMatrix = _xformSystem.GetWorldMatrix(gridXform.Owner); + var ourGridMatrix = _xformSystem.GetWorldMatrix(GridEntity.Value); var dockMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, Angle.Zero); var worldFromDock = Matrix3x2.Multiply(dockMatrix, ourGridMatrix); diff --git a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs index 8bd4a338cb..53ad4a0b23 100644 --- a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs +++ b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs @@ -519,7 +519,7 @@ public sealed partial class ShuttleMapControl : BaseShuttleControl if (mapO is not ShuttleBeaconObject beacon) continue; - var beaconCoords = EntManager.GetCoordinates(beacon.Coordinates).ToMap(EntManager, _xformSystem); + var beaconCoords = _xformSystem.ToMapCoordinates(EntManager.GetCoordinates(beacon.Coordinates)); var position = Vector2.Transform(beaconCoords.Position, mapTransform); var localPos = ScalePosition(position with {Y = -position.Y}); diff --git a/Content.Client/Stack/StackSystem.cs b/Content.Client/Stack/StackSystem.cs index c081581338..7e681aeba3 100644 --- a/Content.Client/Stack/StackSystem.cs +++ b/Content.Client/Stack/StackSystem.cs @@ -44,7 +44,7 @@ namespace Content.Client.Stack // TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call. if (component.Count <= 0 && !component.Lingering) { - Xform.DetachParentToNull(uid, Transform(uid)); + Xform.DetachEntity(uid, Transform(uid)); return; } diff --git a/Content.Client/Storage/Systems/StorageSystem.cs b/Content.Client/Storage/Systems/StorageSystem.cs index a74a118cd0..eea7b9ec79 100644 --- a/Content.Client/Storage/Systems/StorageSystem.cs +++ b/Content.Client/Storage/Systems/StorageSystem.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Numerics; using Content.Client.Animations; using Content.Shared.Hands; @@ -142,14 +142,14 @@ public sealed class StorageSystem : SharedStorageSystem { if (!_timing.IsFirstTimePredicted) return; - - if (finalCoords.InRange(EntityManager, TransformSystem, initialCoords, 0.1f) || + + if (TransformSystem.InRange(finalCoords, initialCoords, 0.1f) || !Exists(initialCoords.EntityId) || !Exists(finalCoords.EntityId)) { return; } - var finalMapPos = finalCoords.ToMapPos(EntityManager, TransformSystem); + var finalMapPos = TransformSystem.ToMapCoordinates(finalCoords).Position; var finalPos = Vector2.Transform(finalMapPos, TransformSystem.GetInvWorldMatrix(initialCoords.EntityId)); _entityPickupAnimation.AnimateEntityPickup(item, initialCoords, finalPos, initialAngle); diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index eba4f21acb..7604d5f880 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -220,7 +220,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem return; } - var targetMap = coordinates.ToMap(EntityManager, TransformSystem); + var targetMap = TransformSystem.ToMapCoordinates(coordinates); if (targetMap.MapId != userXform.MapID) return; diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index ac5914d47c..840d1a9091 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -176,7 +176,7 @@ public sealed partial class GunSystem : SharedGunSystem } // Define target coordinates relative to gun entity, so that network latency on moving grids doesn't fuck up the target location. - var coordinates = EntityCoordinates.FromMap(entity, mousePos, TransformSystem, EntityManager); + var coordinates = TransformSystem.ToCoordinates(entity, mousePos); NetEntity? target = null; if (_state.CurrentState is GameplayStateBase screen) @@ -200,7 +200,7 @@ public sealed partial class GunSystem : SharedGunSystem // Rather than splitting client / server for every ammo provider it's easier // to just delete the spawned entities. This is for programmer sanity despite the wasted perf. // This also means any ammo specific stuff can be grabbed as necessary. - var direction = fromCoordinates.ToMapPos(EntityManager, TransformSystem) - toCoordinates.ToMapPos(EntityManager, TransformSystem); + var direction = TransformSystem.ToMapCoordinates(fromCoordinates).Position - TransformSystem.ToMapCoordinates(toCoordinates).Position; var worldAngle = direction.ToAngle().Opposite(); foreach (var (ent, shootable) in ammo) @@ -276,6 +276,14 @@ public sealed partial class GunSystem : SharedGunSystem if (!Timing.IsFirstTimePredicted) return; + // EntityUid check added to stop throwing exceptions due to https://github.com/space-wizards/space-station-14/issues/28252 + // TODO: Check to see why invalid entities are firing effects. + if (gunUid == EntityUid.Invalid) + { + Log.Debug($"Invalid Entity sent MuzzleFlashEvent (proto: {message.Prototype}, user: {user})"); + return; + } + var gunXform = Transform(gunUid); var gridUid = gunXform.GridUid; EntityCoordinates coordinates; @@ -375,6 +383,6 @@ public sealed partial class GunSystem : SharedGunSystem var uidPlayer = EnsureComp(gunUid); _animPlayer.Stop(gunUid, uidPlayer, "muzzle-flash-light"); - _animPlayer.Play((gunUid, uidPlayer), animTwo,"muzzle-flash-light"); + _animPlayer.Play((gunUid, uidPlayer), animTwo, "muzzle-flash-light"); } } diff --git a/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs b/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs new file mode 100644 index 0000000000..fa1c6c0fed --- /dev/null +++ b/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs @@ -0,0 +1,133 @@ +using System.Linq; +using Content.Server.Antag.Components; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Mind; +using Content.Server.Roles; +using Content.Shared.GameTicking; +using Content.Shared.GameTicking.Components; +using Content.Shared.Mind; +using Content.Shared.NPC.Systems; +using Content.Shared.Objectives.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.GameRules; + +[TestFixture] +public sealed class TraitorRuleTest +{/* + private const string TraitorGameRuleProtoId = "Traitor"; + private const string TraitorAntagRoleName = "Traitor"; + + [Test] + public async Task TestTraitorObjectives() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings() + { + Dirty = true, + DummyTicker = false, + Connected = true, + InLobby = true, + }); + var server = pair.Server; + var client = pair.Client; + var entMan = server.EntMan; + var protoMan = server.ProtoMan; + var compFact = server.ResolveDependency(); + var ticker = server.System(); + var mindSys = server.System(); + var roleSys = server.System(); + var factionSys = server.System(); + var traitorRuleSys = server.System(); + + // Look up the minimum player count and max total objective difficulty for the game rule + var minPlayers = 1; + var maxDifficulty = 0f; + await server.WaitAssertion(() => + { + Assert.That(protoMan.TryIndex(TraitorGameRuleProtoId, out var gameRuleEnt), + $"Failed to lookup traitor game rule entity prototype with ID \"{TraitorGameRuleProtoId}\"!"); + + Assert.That(gameRuleEnt.TryGetComponent(out var gameRule, compFact), + $"Game rule entity {TraitorGameRuleProtoId} does not have a GameRuleComponent!"); + + Assert.That(gameRuleEnt.TryGetComponent(out var randomObjectives, compFact), + $"Game rule entity {TraitorGameRuleProtoId} does not have an AntagRandomObjectivesComponent!"); + + minPlayers = gameRule.MinPlayers; + maxDifficulty = randomObjectives.MaxDifficulty; + }); + + // Initially in the lobby + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); + Assert.That(client.AttachedEntity, Is.Null); + Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay)); + + // Add enough dummy players for the game rule + var dummies = await pair.Server.AddDummySessions(minPlayers); + await pair.RunTicksSync(5); + + // Initially, the players have no attached entities + Assert.That(pair.Player?.AttachedEntity, Is.Null); + Assert.That(dummies.All(x => x.AttachedEntity == null)); + + // Opt-in the player for the traitor role + await pair.SetAntagPreference(TraitorAntagRoleName, true); + + // Add the game rule + var gameRuleEnt = ticker.AddGameRule(TraitorGameRuleProtoId); + Assert.That(entMan.TryGetComponent(gameRuleEnt, out var traitorRule)); + + // Ready up + ticker.ToggleReadyAll(true); + Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.ReadyToPlay)); + + // Start the round + await server.WaitPost(() => + { + ticker.StartRound(); + // Force traitor mode to start (skip the delay) + ticker.StartGameRule(gameRuleEnt); + }); + await pair.RunTicksSync(10); + + // Game should have started + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound)); + Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.JoinedGame)); + Assert.That(client.EntMan.EntityExists(client.AttachedEntity)); + + // Check the player and dummies are spawned + var dummyEnts = dummies.Select(x => x.AttachedEntity ?? default).ToArray(); + var player = pair.Player!.AttachedEntity!.Value; + Assert.That(entMan.EntityExists(player)); + Assert.That(dummyEnts.All(entMan.EntityExists)); + + // Make sure the player is a traitor. + var mind = mindSys.GetMind(player)!.Value; + Assert.That(roleSys.MindIsAntagonist(mind)); + Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True); + Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False); + Assert.That(traitorRule.TotalTraitors, Is.EqualTo(1)); + Assert.That(traitorRule.TraitorMinds[0], Is.EqualTo(mind)); + + // Check total objective difficulty + Assert.That(entMan.TryGetComponent(mind, out var mindComp)); + var totalDifficulty = mindComp.Objectives.Sum(o => entMan.GetComponent(o).Difficulty); + Assert.That(totalDifficulty, Is.AtMost(maxDifficulty), + $"MaxDifficulty exceeded! Objectives: {string.Join(", ", mindComp.Objectives.Select(o => FormatObjective(o, entMan)))}"); + Assert.That(mindComp.Objectives, Is.Not.Empty, + $"No objectives assigned!"); + + + await pair.CleanReturnAsync(); + } + + private static string FormatObjective(Entity entity, IEntityManager entMan) + { + var meta = entMan.GetComponent(entity); + var objective = entMan.GetComponent(entity); + return $"{meta.EntityName} ({objective.Difficulty})"; + }*/ //Disabled in CP14 +} diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs index 053152dbe1..194bc54fba 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs @@ -114,7 +114,7 @@ public abstract partial class InteractionTest return await SpawnEntity((stack.StackTypeId, spec.Quantity), coords); Assert.That(spec.Quantity, Is.EqualTo(1), "SpawnEntity only supports returning a singular entity"); - await Server.WaitPost(() => uid = SEntMan.SpawnEntity(spec.Prototype, coords)); + await Server.WaitPost(() => uid = SEntMan.SpawnAtPosition(spec.Prototype, coords)); return uid; } diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs index 33e4da3fa3..0f2c314ed0 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs @@ -91,7 +91,7 @@ public abstract partial class InteractionTest Target = NetEntity.Invalid; await Server.WaitPost(() => { - Target = SEntMan.GetNetEntity(SEntMan.SpawnEntity(prototype, SEntMan.GetCoordinates(TargetCoords))); + Target = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(TargetCoords))); }); await RunTicks(5); diff --git a/Content.Server/Administration/Commands/AGhost.cs b/Content.Server/Administration/Commands/AGhostCommand.cs similarity index 97% rename from Content.Server/Administration/Commands/AGhost.cs rename to Content.Server/Administration/Commands/AGhostCommand.cs index 935114e7a6..b24dbbc018 100644 --- a/Content.Server/Administration/Commands/AGhost.cs +++ b/Content.Server/Administration/Commands/AGhostCommand.cs @@ -12,13 +12,12 @@ using Robust.Shared.Console; namespace Content.Server.Administration.Commands; [AdminCommand(AdminFlags.Admin)] -public sealed class AGhost : LocalizedCommands +public sealed class AGhostCommand : LocalizedCommands { [Dependency] private readonly IEntityManager _entities = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; public override string Command => "aghost"; - public override string Description => LocalizationManager.GetString("aghost-description"); public override string Help => "aghost"; public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) diff --git a/Content.Server/Antag/AntagRandomObjectivesSystem.cs b/Content.Server/Antag/AntagRandomObjectivesSystem.cs index c935b8c064..b60759a3d5 100644 --- a/Content.Server/Antag/AntagRandomObjectivesSystem.cs +++ b/Content.Server/Antag/AntagRandomObjectivesSystem.cs @@ -39,7 +39,8 @@ public sealed class AntagRandomObjectivesSystem : EntitySystem for (var pick = 0; pick < set.MaxPicks && ent.Comp.MaxDifficulty > difficulty; pick++) { - if (_objectives.GetRandomObjective(mindId, mind, set.Groups) is not {} objective) + var remainingDifficulty = ent.Comp.MaxDifficulty - difficulty; + if (_objectives.GetRandomObjective(mindId, mind, set.Groups, remainingDifficulty) is not { } objective) continue; _mind.AddObjective(mindId, mind, objective); diff --git a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs index c2cdd4a107..0f4490cd7e 100644 --- a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs @@ -162,7 +162,7 @@ namespace Content.Server.Atmos.EntitySystems if (component.LastPosition.HasValue) { // Check if position is out of range => don't update and disable - if (!component.LastPosition.Value.InRange(EntityManager, _transform, userPos, SharedInteractionSystem.InteractionRange)) + if (!_transform.InRange(component.LastPosition.Value, userPos, SharedInteractionSystem.InteractionRange)) { if (component.User is { } userId && component.Enabled) _popup.PopupEntity(Loc.GetString("gas-analyzer-shutoff"), userId, userId); diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index baad739804..2d703439c0 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -81,7 +81,7 @@ namespace Content.Server.Atmos.EntitySystems TankPressure = component.Air?.Pressure ?? 0, OutputPressure = initialUpdate ? component.OutputPressure : null, InternalsConnected = component.IsConnected, - CanConnectInternals = CanConnectToInternals(component) + CanConnectInternals = CanConnectToInternals(ent) }); } @@ -217,24 +217,24 @@ namespace Content.Server.Atmos.EntitySystems return air; } - public bool CanConnectToInternals(GasTankComponent component) + public bool CanConnectToInternals(Entity ent) { - var internals = GetInternalsComponent(component, component.User); - return internals != null && internals.BreathTools.Count != 0 && !component.IsValveOpen; + TryGetInternalsComp(ent, out _, out var internalsComp, ent.Comp.User); + return internalsComp != null && internalsComp.BreathTools.Count != 0 && !ent.Comp.IsValveOpen; } public void ConnectToInternals(Entity ent) { var (owner, component) = ent; - if (component.IsConnected || !CanConnectToInternals(component)) + if (component.IsConnected || !CanConnectToInternals(ent)) return; - var internals = GetInternalsComponent(component); - if (internals == null) + TryGetInternalsComp(ent, out var internalsUid, out var internalsComp, ent.Comp.User); + if (internalsUid == null || internalsComp == null) return; - if (_internals.TryConnectTank((internals.Owner, internals), owner)) - component.User = internals.Owner; + if (_internals.TryConnectTank((internalsUid.Value, internalsComp), owner)) + component.User = internalsUid.Value; _actions.SetToggled(component.ToggleActionEntity, component.IsConnected); @@ -243,7 +243,7 @@ namespace Content.Server.Atmos.EntitySystems return; component.ConnectStream = _audioSys.Stop(component.ConnectStream); - component.ConnectStream = _audioSys.PlayPvs(component.ConnectSound, component.Owner)?.Entity; + component.ConnectStream = _audioSys.PlayPvs(component.ConnectSound, owner)?.Entity; UpdateUserInterface(ent); } @@ -251,29 +251,59 @@ namespace Content.Server.Atmos.EntitySystems public void DisconnectFromInternals(Entity ent) { var (owner, component) = ent; + if (component.User == null) return; - var internals = GetInternalsComponent(component); + TryGetInternalsComp(ent, out var internalsUid, out var internalsComp, component.User); component.User = null; _actions.SetToggled(component.ToggleActionEntity, false); - _internals.DisconnectTank(internals); + if (internalsUid != null && internalsComp != null) + _internals.DisconnectTank((internalsUid.Value, internalsComp)); component.DisconnectStream = _audioSys.Stop(component.DisconnectStream); - component.DisconnectStream = _audioSys.PlayPvs(component.DisconnectSound, component.Owner)?.Entity; + component.DisconnectStream = _audioSys.PlayPvs(component.DisconnectSound, owner)?.Entity; UpdateUserInterface(ent); } - private InternalsComponent? GetInternalsComponent(GasTankComponent component, EntityUid? owner = null) + /// + /// Tries to retrieve the internals component of either the gas tank's user, + /// or the gas tank's... containing container + /// + /// The user of the gas tank + /// True if internals comp isn't null, false if it is null + private bool TryGetInternalsComp(Entity ent, out EntityUid? internalsUid, out InternalsComponent? internalsComp, EntityUid? user = null) { - owner ??= component.User; - if (Deleted(component.Owner))return null; - if (owner != null) return CompOrNull(owner.Value); - return _containers.TryGetContainingContainer(component.Owner, out var container) - ? CompOrNull(container.Owner) - : null; + internalsUid = default; + internalsComp = default; + + // If the gas tank doesn't exist for whatever reason, don't even bother + if (TerminatingOrDeleted(ent.Owner)) + return false; + + user ??= ent.Comp.User; + // Check if the gas tank's user actually has the component that allows them to use a gas tank and mask + if (TryComp(user, out var userInternalsComp) && userInternalsComp != null) + { + internalsUid = user; + internalsComp = userInternalsComp; + return true; + } + + // Yeah I have no clue what this actually does, I appreciate the lack of comments on the original function + if (_containers.TryGetContainingContainer((ent.Owner, Transform(ent.Owner)), out var container) && container != null) + { + if (TryComp(container.Owner, out var containerInternalsComp) && containerInternalsComp != null) + { + internalsUid = container.Owner; + internalsComp = containerInternalsComp; + return true; + } + } + + return false; } public void AssumeAir(Entity ent, GasMixture giver) @@ -321,7 +351,7 @@ namespace Content.Server.Atmos.EntitySystems if(environment != null) _atmosphereSystem.Merge(environment, component.Air); - _audioSys.PlayPvs(component.RuptureSound, Transform(component.Owner).Coordinates, AudioParams.Default.WithVariation(0.125f)); + _audioSys.PlayPvs(component.RuptureSound, Transform(owner).Coordinates, AudioParams.Default.WithVariation(0.125f)); QueueDel(owner); return; diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index d6ece39d59..10ebd934aa 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -102,7 +102,7 @@ public sealed class InternalsSystem : EntitySystem { if (force) { - DisconnectTank(internals); + DisconnectTank((uid, internals)); return; } @@ -199,16 +199,13 @@ public sealed class InternalsSystem : EntitySystem _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } - public void DisconnectTank(InternalsComponent? component) + public void DisconnectTank(Entity ent) { - if (component is null) - return; + if (TryComp(ent.Comp.GasTankEntity, out GasTankComponent? tank)) + _gasTank.DisconnectFromInternals((ent.Comp.GasTankEntity.Value, tank)); - if (TryComp(component.GasTankEntity, out GasTankComponent? tank)) - _gasTank.DisconnectFromInternals((component.GasTankEntity.Value, tank)); - - component.GasTankEntity = null; - _alerts.ShowAlert(component.Owner, component.InternalsAlert, GetSeverity(component)); + ent.Comp.GasTankEntity = null; + _alerts.ShowAlert(ent.Owner, ent.Comp.InternalsAlert, GetSeverity(ent.Comp)); } public bool TryConnectTank(Entity ent, EntityUid tankEntity) @@ -267,21 +264,21 @@ public sealed class InternalsSystem : EntitySystem if (_inventory.TryGetSlotEntity(user, "back", out var backEntity, user.Comp2, user.Comp3) && TryComp(backEntity, out var backGasTank) && - _gasTank.CanConnectToInternals(backGasTank)) + _gasTank.CanConnectToInternals((backEntity.Value, backGasTank))) { return (backEntity.Value, backGasTank); } if (_inventory.TryGetSlotEntity(user, "suitstorage", out var entity, user.Comp2, user.Comp3) && TryComp(entity, out var gasTank) && - _gasTank.CanConnectToInternals(gasTank)) + _gasTank.CanConnectToInternals((entity.Value, gasTank))) { return (entity.Value, gasTank); } foreach (var item in _inventory.GetHandOrInventoryEntities((user.Owner, user.Comp1, user.Comp2))) { - if (TryComp(item, out gasTank) && _gasTank.CanConnectToInternals(gasTank)) + if (TryComp(item, out gasTank) && _gasTank.CanConnectToInternals((item, gasTank))) return (item, gasTank); } diff --git a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs index a288d7b07d..dd408755e6 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs @@ -40,6 +40,7 @@ namespace Content.Server.Cargo.Systems SubscribeLocalEvent(OnOrderUIOpened); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnOrderBalanceUpdated); Reset(); } @@ -315,6 +316,15 @@ namespace Content.Server.Cargo.Systems #endregion + + private void OnOrderBalanceUpdated(Entity ent, ref BankBalanceUpdatedEvent args) + { + if (!_uiSystem.IsUiOpen(ent.Owner, CargoConsoleUiKey.Orders)) + return; + + UpdateOrderState(ent, args.Station); + } + private void UpdateOrderState(EntityUid consoleUid, EntityUid? station) { if (station == null || diff --git a/Content.Server/Cargo/Systems/CargoSystem.cs b/Content.Server/Cargo/Systems/CargoSystem.cs index a93a7bdcc2..1b33404e35 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.cs @@ -81,18 +81,18 @@ public sealed partial class CargoSystem : SharedCargoSystem public void UpdateBankAccount(EntityUid uid, StationBankAccountComponent component, int balanceAdded) { component.Balance += balanceAdded; - var query = EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); - while (query.MoveNext(out var oUid, out var _)) + var ev = new BankBalanceUpdatedEvent(uid, component.Balance); + while (query.MoveNext(out var client, out var comp, out var xform)) { - if (!_uiSystem.IsUiOpen(oUid, CargoConsoleUiKey.Orders)) - continue; - - var station = _station.GetOwningStation(oUid); + var station = _station.GetOwningStation(client, xform); if (station != uid) continue; - UpdateOrderState(oUid, station); + comp.Balance = component.Balance; + Dirty(client, comp); + RaiseLocalEvent(client, ref ev); } } } diff --git a/Content.Server/Clock/ClockSystem.cs b/Content.Server/Clock/ClockSystem.cs new file mode 100644 index 0000000000..29400a59f7 --- /dev/null +++ b/Content.Server/Clock/ClockSystem.cs @@ -0,0 +1,42 @@ +using Content.Server.GameTicking.Events; +using Content.Shared.Clock; +using Content.Shared.Destructible; +using Robust.Server.GameStates; +using Robust.Shared.Random; + +namespace Content.Server.Clock; + +public sealed class ClockSystem : SharedClockSystem +{ + [Dependency] private readonly PvsOverrideSystem _pvsOverride = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnRoundStart); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnBreak); + } + + private void OnRoundStart(RoundStartingEvent ev) + { + var manager = Spawn(); + AddComp(manager); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.TimeOffset = TimeSpan.FromHours(_robustRandom.NextFloat(0, 24)); + _pvsOverride.AddGlobalOverride(ent); + Dirty(ent); + } + + private void OnBreak(Entity ent, ref BreakageEventArgs args) + { + ent.Comp.StuckTime = GetClockTime(ent); + Dirty(ent, ent.Comp); + } +} diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs index 62d1f61a35..96ca8d3614 100644 --- a/Content.Server/Dragon/DragonSystem.cs +++ b/Content.Server/Dragon/DragonSystem.cs @@ -146,7 +146,7 @@ public sealed partial class DragonSystem : EntitySystem // cant stack rifts near eachother foreach (var (_, riftXform) in EntityQuery(true)) { - if (riftXform.Coordinates.InRange(EntityManager, _transform, xform.Coordinates, RiftRange)) + if (_transform.InRange(riftXform.Coordinates, xform.Coordinates, RiftRange)) { _popup.PopupEntity(Loc.GetString("carp-rift-proximity", ("proximity", RiftRange)), uid, uid); return; diff --git a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs index 281bbc4721..8391e8faad 100644 --- a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs +++ b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs @@ -65,8 +65,8 @@ namespace Content.Server.Engineering.EntitySystems EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid)); - if (component.RemoveOnInteract && stackComp == null && !((!EntityManager.EntityExists(uid) ? EntityLifeStage.Deleted : EntityManager.GetComponent(component.Owner).EntityLifeStage) >= EntityLifeStage.Deleted)) - EntityManager.DeleteEntity(uid); + if (component.RemoveOnInteract && stackComp == null) + TryQueueDel(uid); } } } diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.TimedCollide.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.TimedCollide.cs index 29b81deb32..ea10b7c69b 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.TimedCollide.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.TimedCollide.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Content.Server.Explosion.Components; using Content.Server.Explosion.EntitySystems; using Robust.Shared.Physics.Dynamics; @@ -42,14 +42,15 @@ public sealed partial class TriggerSystem private void UpdateTimedCollide(float frameTime) { - foreach (var (activeTrigger, triggerOnTimedCollide) in EntityQuery()) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out var triggerOnTimedCollide)) { foreach (var (collidingEntity, collidingTimer) in triggerOnTimedCollide.Colliding) { triggerOnTimedCollide.Colliding[collidingEntity] += frameTime; if (collidingTimer > triggerOnTimedCollide.Threshold) { - RaiseLocalEvent(activeTrigger.Owner, new TriggerEvent(activeTrigger.Owner, collidingEntity), true); + RaiseLocalEvent(uid, new TriggerEvent(uid, collidingEntity), true); triggerOnTimedCollide.Colliding[collidingEntity] -= triggerOnTimedCollide.Threshold; } } diff --git a/Content.Server/Extinguisher/FireExtinguisherComponent.cs b/Content.Server/Extinguisher/FireExtinguisherComponent.cs deleted file mode 100644 index 991fc76c62..0000000000 --- a/Content.Server/Extinguisher/FireExtinguisherComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Content.Shared.Extinguisher; -using Robust.Shared.GameStates; - -namespace Content.Server.Extinguisher; - -[RegisterComponent] -[Access(typeof(FireExtinguisherSystem))] -public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent -{ -} diff --git a/Content.Server/Extinguisher/FireExtinguisherSystem.cs b/Content.Server/Extinguisher/FireExtinguisherSystem.cs deleted file mode 100644 index b33a1af157..0000000000 --- a/Content.Server/Extinguisher/FireExtinguisherSystem.cs +++ /dev/null @@ -1,139 +0,0 @@ -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Fluids.EntitySystems; -using Content.Server.Popups; -using Content.Shared.Chemistry.Components; -using Content.Shared.Extinguisher; -using Content.Shared.FixedPoint; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Verbs; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; - -namespace Content.Server.Extinguisher; - -public sealed class FireExtinguisherSystem : EntitySystem -{ - [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnFireExtinguisherInit); - SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent>(OnGetInteractionVerbs); - SubscribeLocalEvent(OnSprayAttempt); - } - - private void OnFireExtinguisherInit(Entity entity, ref ComponentInit args) - { - if (entity.Comp.HasSafety) - { - UpdateAppearance((entity.Owner, entity.Comp)); - } - } - - private void OnUseInHand(Entity entity, ref UseInHandEvent args) - { - if (args.Handled) - return; - - ToggleSafety((entity.Owner, entity.Comp), args.User); - - args.Handled = true; - } - - private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) - { - if (args.Target == null || !args.CanReach) - { - return; - } - - if (args.Handled) - return; - - if (entity.Comp.HasSafety && entity.Comp.Safety) - { - _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), entity.Owner, args.User); - return; - } - - if (args.Target is not { Valid: true } target || - !_solutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out var targetSolution) || - !_solutionContainerSystem.TryGetRefillableSolution(entity.Owner, out var containerSoln, out var containerSolution)) - { - return; - } - - args.Handled = true; - - // TODO: why is this copy paste shit here just have fire extinguisher cancel transfer when safety is on - var transfer = containerSolution.AvailableVolume; - if (TryComp(entity.Owner, out var solTrans)) - { - transfer = solTrans.TransferAmount; - } - transfer = FixedPoint2.Min(transfer, targetSolution.Volume); - - if (transfer > 0) - { - var drained = _solutionContainerSystem.Drain(target, targetSoln.Value, transfer); - _solutionContainerSystem.TryAddSolution(containerSoln.Value, drained); - - _audio.PlayPvs(entity.Comp.RefillSound, entity.Owner); - _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-after-interact-refilled-message", ("owner", entity.Owner)), - entity.Owner, args.Target.Value); - } - } - - private void OnGetInteractionVerbs(Entity entity, ref GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - var user = args.User; - var verb = new InteractionVerb - { - Act = () => ToggleSafety((entity.Owner, entity.Comp), user), - Text = Loc.GetString("fire-extinguisher-component-verb-text"), - }; - - args.Verbs.Add(verb); - } - - private void OnSprayAttempt(Entity entity, ref SprayAttemptEvent args) - { - if (entity.Comp.HasSafety && entity.Comp.Safety) - { - _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), entity, args.User); - args.Cancel(); - } - } - - private void UpdateAppearance(Entity entity) - { - if (!Resolve(entity, ref entity.Comp2, false)) - return; - - if (entity.Comp1.HasSafety) - { - _appearance.SetData(entity, FireExtinguisherVisuals.Safety, entity.Comp1.Safety, entity.Comp2); - } - } - - public void ToggleSafety(Entity extinguisher, EntityUid user) - { - if (!Resolve(extinguisher, ref extinguisher.Comp)) - return; - - extinguisher.Comp.Safety = !extinguisher.Comp.Safety; - _audio.PlayPvs(extinguisher.Comp.SafetySound, extinguisher, AudioParams.Default.WithVariation(0.125f).WithVolume(-4f)); - UpdateAppearance((extinguisher.Owner, extinguisher.Comp)); - } -} diff --git a/Content.Server/Fluids/EntitySystems/SpraySystem.cs b/Content.Server/Fluids/EntitySystems/SpraySystem.cs index 5499070738..215ed7c33f 100644 --- a/Content.Server/Fluids/EntitySystems/SpraySystem.cs +++ b/Content.Server/Fluids/EntitySystems/SpraySystem.cs @@ -1,11 +1,11 @@ using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Chemistry.EntitySystems; -using Content.Server.Extinguisher; using Content.Server.Fluids.Components; using Content.Server.Gravity; using Content.Server.Popups; using Content.Shared.FixedPoint; +using Content.Shared.Fluids; using Content.Shared.Interaction; using Content.Shared.Timing; using Content.Shared.Vapor; @@ -34,7 +34,7 @@ public sealed class SpraySystem : EntitySystem { base.Initialize(); - SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(FireExtinguisherSystem) }); + SubscribeLocalEvent(OnAfterInteract); } private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) @@ -48,7 +48,7 @@ public sealed class SpraySystem : EntitySystem return; var ev = new SprayAttemptEvent(args.User); - RaiseLocalEvent(entity, ev); + RaiseLocalEvent(entity, ref ev); if (ev.Cancelled) return; @@ -148,13 +148,3 @@ public sealed class SpraySystem : EntitySystem _useDelay.TryResetDelay((entity, useDelay)); } } - -public sealed class SprayAttemptEvent : CancellableEntityEventArgs -{ - public EntityUid User; - - public SprayAttemptEvent(EntityUid user) - { - User = user; - } -} diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs index 203882ed9e..ae4d0ca2b8 100644 --- a/Content.Server/Guardian/GuardianSystem.cs +++ b/Content.Server/Guardian/GuardianSystem.cs @@ -325,7 +325,7 @@ namespace Content.Server.Guardian if (!guardianComponent.GuardianLoose) return; - if (!guardianXform.Coordinates.InRange(EntityManager, _transform, hostXform.Coordinates, guardianComponent.DistanceAllowed)) + if (!_transform.InRange(guardianXform.Coordinates, hostXform.Coordinates, guardianComponent.DistanceAllowed)) RetractGuardian(hostUid, hostComponent, guardianUid, guardianComponent); } diff --git a/Content.Server/Instruments/InstrumentSystem.cs b/Content.Server/Instruments/InstrumentSystem.cs index 582bf7fa67..6814b596dc 100644 --- a/Content.Server/Instruments/InstrumentSystem.cs +++ b/Content.Server/Instruments/InstrumentSystem.cs @@ -402,7 +402,8 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem var trans = transformQuery.GetComponent(uid); var masterTrans = transformQuery.GetComponent(master); - if (!masterTrans.Coordinates.InRange(EntityManager, _transform, trans.Coordinates, 10f)) + if (!_transform.InRange(masterTrans.Coordinates, trans.Coordinates, 10f) +) { Clean(uid, instrument); } diff --git a/Content.Server/Medical/HealthAnalyzerSystem.cs b/Content.Server/Medical/HealthAnalyzerSystem.cs index 1d6e564a32..98f4f00d89 100644 --- a/Content.Server/Medical/HealthAnalyzerSystem.cs +++ b/Content.Server/Medical/HealthAnalyzerSystem.cs @@ -65,7 +65,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem //Get distance between health analyzer and the scanned entity var patientCoordinates = Transform(patient).Coordinates; - if (!patientCoordinates.InRange(EntityManager, _transformSystem, transform.Coordinates, component.MaxScanRange)) + if (!_transformSystem.InRange(patientCoordinates, transform.Coordinates, component.MaxScanRange)) { //Range too far, disable updates StopAnalyzingEntity((uid, component), patient); diff --git a/Content.Server/Movement/Systems/PullController.cs b/Content.Server/Movement/Systems/PullController.cs index 340dc5654e..4bd4b60371 100644 --- a/Content.Server/Movement/Systems/PullController.cs +++ b/Content.Server/Movement/Systems/PullController.cs @@ -58,6 +58,7 @@ public sealed class PullController : VirtualController [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; /// /// If distance between puller and pulled entity lower that this threshold, @@ -133,8 +134,8 @@ public sealed class PullController : VirtualController var range = 2f; var fromUserCoords = coords.WithEntityId(player, EntityManager); var userCoords = new EntityCoordinates(player, Vector2.Zero); - - if (!coords.InRange(EntityManager, TransformSystem, userCoords, range)) + + if (!_transformSystem.InRange(coords, userCoords, range)) { var direction = fromUserCoords.Position - userCoords.Position; diff --git a/Content.Server/NPC/HTN/HTNPlanJob.cs b/Content.Server/NPC/HTN/HTNPlanJob.cs index d6d10ad311..8158303524 100644 --- a/Content.Server/NPC/HTN/HTNPlanJob.cs +++ b/Content.Server/NPC/HTN/HTNPlanJob.cs @@ -160,9 +160,9 @@ public sealed class HTNPlanJob : Job { var compound = _protoManager.Index(compoundId.Task); - for (var i = mtrIndex; i < compound.Branches.Count; i++) + for (; mtrIndex < compound.Branches.Count; mtrIndex++) { - var branch = compound.Branches[i]; + var branch = compound.Branches[mtrIndex]; var isValid = true; foreach (var con in branch.Preconditions) diff --git a/Content.Server/NPC/HTN/Preconditions/CoordinatesInRangePrecondition.cs b/Content.Server/NPC/HTN/Preconditions/CoordinatesInRangePrecondition.cs index 3485bd2a18..452bf327f2 100644 --- a/Content.Server/NPC/HTN/Preconditions/CoordinatesInRangePrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/CoordinatesInRangePrecondition.cs @@ -8,12 +8,19 @@ namespace Content.Server.NPC.HTN.Preconditions; public sealed partial class CoordinatesInRangePrecondition : HTNPrecondition { [Dependency] private readonly IEntityManager _entManager = default!; + private SharedTransformSystem _transformSystem = default!; [DataField("targetKey", required: true)] public string TargetKey = default!; [DataField("rangeKey", required: true)] public string RangeKey = default!; + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _transformSystem = sysManager.GetEntitySystem(); + } + public override bool IsMet(NPCBlackboard blackboard) { if (!blackboard.TryGetValue(NPCBlackboard.OwnerCoordinates, out var coordinates, _entManager)) @@ -22,6 +29,6 @@ public sealed partial class CoordinatesInRangePrecondition : HTNPrecondition if (!blackboard.TryGetValue(TargetKey, out var target, _entManager)) return false; - return coordinates.InRange(_entManager, _entManager.System(), target, blackboard.GetValueOrDefault(RangeKey, _entManager)); + return _transformSystem.InRange(coordinates, target, blackboard.GetValueOrDefault(RangeKey, _entManager)); } } diff --git a/Content.Server/NPC/HTN/Preconditions/CoordinatesNotInRangePrecondition.cs b/Content.Server/NPC/HTN/Preconditions/CoordinatesNotInRangePrecondition.cs index 9d000ca2eb..901831679e 100644 --- a/Content.Server/NPC/HTN/Preconditions/CoordinatesNotInRangePrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/CoordinatesNotInRangePrecondition.cs @@ -8,12 +8,19 @@ namespace Content.Server.NPC.HTN.Preconditions; public sealed partial class CoordinatesNotInRangePrecondition : HTNPrecondition { [Dependency] private readonly IEntityManager _entManager = default!; + private SharedTransformSystem _transformSystem = default!; [DataField("targetKey", required: true)] public string TargetKey = default!; [DataField("rangeKey", required: true)] public string RangeKey = default!; + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _transformSystem = sysManager.GetEntitySystem(); + } + public override bool IsMet(NPCBlackboard blackboard) { if (!blackboard.TryGetValue(NPCBlackboard.OwnerCoordinates, out var coordinates, _entManager)) @@ -22,7 +29,7 @@ public sealed partial class CoordinatesNotInRangePrecondition : HTNPrecondition if (!blackboard.TryGetValue(TargetKey, out var target, _entManager)) return false; - return !coordinates.InRange(_entManager, _entManager.System(), target, blackboard.GetValueOrDefault(RangeKey, _entManager)); + return !_transformSystem.InRange(coordinates, target, blackboard.GetValueOrDefault(RangeKey, _entManager)); } } diff --git a/Content.Server/NPC/HTN/Preconditions/TargetInRangePrecondition.cs b/Content.Server/NPC/HTN/Preconditions/TargetInRangePrecondition.cs index aaccb426d7..921b5ffa22 100644 --- a/Content.Server/NPC/HTN/Preconditions/TargetInRangePrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/TargetInRangePrecondition.cs @@ -8,11 +8,17 @@ namespace Content.Server.NPC.HTN.Preconditions; public sealed partial class TargetInRangePrecondition : HTNPrecondition { [Dependency] private readonly IEntityManager _entManager = default!; + private SharedTransformSystem _transformSystem = default!; [DataField("targetKey", required: true)] public string TargetKey = default!; [DataField("rangeKey", required: true)] public string RangeKey = default!; + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _transformSystem = sysManager.GetEntitySystem(); + } public override bool IsMet(NPCBlackboard blackboard) { @@ -23,6 +29,7 @@ public sealed partial class TargetInRangePrecondition : HTNPrecondition !_entManager.TryGetComponent(target, out var targetXform)) return false; - return coordinates.InRange(_entManager, _entManager.System(), targetXform.Coordinates, blackboard.GetValueOrDefault(RangeKey, _entManager)); + var transformSystem = _entManager.System; + return _transformSystem.InRange(coordinates, targetXform.Coordinates, blackboard.GetValueOrDefault(RangeKey, _entManager)); } } diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs index 32be027ec4..5a02b86201 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs @@ -89,7 +89,8 @@ public sealed partial class MeleeOperator : HTNOperator, IHtnConditionalShutdown HTNOperatorStatus status; if (_entManager.TryGetComponent(owner, out var combat) && - blackboard.TryGetValue(TargetKey, out var target, _entManager)) + blackboard.TryGetValue(TargetKey, out var target, _entManager) && + target != EntityUid.Invalid) { combat.Target = target; diff --git a/Content.Server/NPC/Queries/Considerations/TargetHealthCon.cs b/Content.Server/NPC/Queries/Considerations/TargetHealthCon.cs index d4e8a277ea..cc9c6df83f 100644 --- a/Content.Server/NPC/Queries/Considerations/TargetHealthCon.cs +++ b/Content.Server/NPC/Queries/Considerations/TargetHealthCon.cs @@ -1,6 +1,16 @@ +using Content.Shared.Mobs; + namespace Content.Server.NPC.Queries.Considerations; +/// +/// Goes linearly from 1f to 0f, with 0 damage returning 1f and damage returning 0f +/// public sealed partial class TargetHealthCon : UtilityConsideration { + /// + /// Which MobState the consideration returns 0f at, defaults to choosing earliest incapacitating MobState + /// + [DataField("targetState")] + public MobState TargetState = MobState.Invalid; } diff --git a/Content.Server/NPC/Systems/NPCJukeSystem.cs b/Content.Server/NPC/Systems/NPCJukeSystem.cs index da9fa1f761..94a30feb0c 100644 --- a/Content.Server/NPC/Systems/NPCJukeSystem.cs +++ b/Content.Server/NPC/Systems/NPCJukeSystem.cs @@ -143,6 +143,9 @@ public sealed class NPCJukeSystem : EntitySystem if (!_melee.TryGetWeapon(uid, out var weaponUid, out var weapon)) return; + if (!HasComp(melee.Target)) + return; + var cdRemaining = weapon.NextAttack - _timing.CurTime; var attackCooldown = TimeSpan.FromSeconds(1f / _melee.GetAttackRate(weaponUid, uid, weapon)); diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index ca74d71335..72833fc35e 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -7,10 +7,13 @@ using Content.Server.NPC.Queries.Queries; using Content.Server.Nutrition.Components; using Content.Server.Nutrition.EntitySystems; using Content.Server.Storage.Components; +using Content.Shared.Damage; using Content.Shared.Examine; using Content.Shared.Fluids.Components; using Content.Shared.Hands.Components; using Content.Shared.Inventory; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.NPC.Systems; using Content.Shared.Nutrition.Components; @@ -48,6 +51,7 @@ public sealed class NPCUtilitySystem : EntitySystem [Dependency] private readonly WeldableSystem _weldable = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly MobThresholdSystem _thresholdSystem = default!; private EntityQuery _puddleQuery; private EntityQuery _xformQuery; @@ -293,8 +297,14 @@ public sealed class NPCUtilitySystem : EntitySystem return (float) ev.Count / ev.Capacity; } - case TargetHealthCon: + case TargetHealthCon con: { + if (!TryComp(targetUid, out DamageableComponent? damage)) + return 0f; + if (con.TargetState != MobState.Invalid && _thresholdSystem.TryGetPercentageForState(targetUid, con.TargetState, damage.TotalDamage, out var percentage)) + return Math.Clamp((float)(1 - percentage), 0f, 1f); + if (_thresholdSystem.TryGetIncapPercentage(targetUid, damage.TotalDamage, out var incapPercentage)) + return Math.Clamp((float)(1 - incapPercentage), 0f, 1f); return 0f; } case TargetInLOSCon: diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs index 3950c73eb4..26d86acd54 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs @@ -42,7 +42,7 @@ namespace Content.Server.Nutrition.EntitySystems if (!isHotEvent.IsHot) return; - if (TryTransferReagents(entity.Comp, smokable)) + if (TryTransferReagents(entity, (entity.Owner, smokable))) SetSmokableState(entity, SmokableState.Lit, smokable); args.Handled = true; } @@ -62,7 +62,7 @@ namespace Content.Server.Nutrition.EntitySystems if (!isHotEvent.IsHot) return; - if (TryTransferReagents(entity.Comp, smokable)) + if (TryTransferReagents(entity, (entity.Owner, smokable))) SetSmokableState(entity, SmokableState.Lit, smokable); args.Handled = true; } @@ -74,15 +74,15 @@ namespace Content.Server.Nutrition.EntitySystems } // Convert smokable item into reagents to be smoked - private bool TryTransferReagents(SmokingPipeComponent component, SmokableComponent smokable) + private bool TryTransferReagents(Entity entity, Entity smokable) { - if (component.BowlSlot.Item == null) + if (entity.Comp.BowlSlot.Item == null) return false; - EntityUid contents = component.BowlSlot.Item.Value; + EntityUid contents = entity.Comp.BowlSlot.Item.Value; if (!TryComp(contents, out var reagents) || - !_solutionContainerSystem.TryGetSolution(smokable.Owner, smokable.Solution, out var pipeSolution, out _)) + !_solutionContainerSystem.TryGetSolution(smokable.Owner, smokable.Comp.Solution, out var pipeSolution, out _)) return false; foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((contents, reagents))) @@ -93,7 +93,7 @@ namespace Content.Server.Nutrition.EntitySystems EntityManager.DeleteEntity(contents); - _itemSlotsSystem.SetLock(component.Owner, component.BowlSlot, true); //no inserting more until current runs out + _itemSlotsSystem.SetLock(entity.Owner, entity.Comp.BowlSlot, true); //no inserting more until current runs out return true; } diff --git a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs index 43087214a4..d40288183f 100644 --- a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs @@ -31,24 +31,24 @@ namespace Content.Server.Nutrition.EntitySystems /// /// Clicked with utensil /// - private void OnAfterInteract(EntityUid uid, UtensilComponent component, AfterInteractEvent ev) + private void OnAfterInteract(Entity entity, ref AfterInteractEvent ev) { if (ev.Handled || ev.Target == null || !ev.CanReach) return; - var result = TryUseUtensil(ev.User, ev.Target.Value, component); + var result = TryUseUtensil(ev.User, ev.Target.Value, entity); ev.Handled = result.Handled; } - public (bool Success, bool Handled) TryUseUtensil(EntityUid user, EntityUid target, UtensilComponent component) + public (bool Success, bool Handled) TryUseUtensil(EntityUid user, EntityUid target, Entity utensil) { if (!EntityManager.TryGetComponent(target, out FoodComponent? food)) return (false, true); //Prevents food usage with a wrong utensil - if ((food.Utensil & component.Types) == 0) + if ((food.Utensil & utensil.Comp.Types) == 0) { - _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", target), ("utensil", component.Owner)), user, user); + _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", target), ("utensil", utensil.Owner)), user, user); return (false, true); } diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index 18077b413a..c9cdf244e6 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -12,6 +12,7 @@ using Robust.Shared.Random; using System.Linq; using System.Text; using Robust.Server.Player; +using Robust.Shared.Utility; namespace Content.Server.Objectives; @@ -180,33 +181,32 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem } } - public EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, string objectiveGroupProto) + public EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, ProtoId objectiveGroupProto, float maxDifficulty) { - if (!_prototypeManager.TryIndex(objectiveGroupProto, out var groups)) + if (!_prototypeManager.TryIndex(objectiveGroupProto, out var groupsProto)) { Log.Error($"Tried to get a random objective, but can't index WeightedRandomPrototype {objectiveGroupProto}"); return null; } - // TODO replace whatever the fuck this is with a proper objective selection system - // yeah the old 'preventing infinite loops' thing wasn't super elegant either and it mislead people on what exactly it did - var tries = 0; - while (tries < 20) - { - var groupName = groups.Pick(_random); + // Make a copy of the weights so we don't trash the prototype by removing entries + var groups = groupsProto.Weights.ShallowClone(); + while (_random.TryPickAndTake(groups, out var groupName)) + { if (!_prototypeManager.TryIndex(groupName, out var group)) { Log.Error($"Couldn't index objective group prototype {groupName}"); return null; } - var proto = group.Pick(_random); - var objective = TryCreateObjective(mindId, mind, proto); - if (objective != null) - return objective; - - tries++; + var objectives = group.Weights.ShallowClone(); + while (_random.TryPickAndTake(objectives, out var objectiveProto)) + { + if (TryCreateObjective((mindId, mind), objectiveProto, out var objective) + && Comp(objective.Value).Difficulty <= maxDifficulty) + return objective; + } } return null; diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index 9dc3b5e1e6..4b7f50fb86 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -101,7 +101,7 @@ namespace Content.Server.Pointing.EntitySystems { if (HasComp(pointer)) { - return Transform(pointer).Coordinates.InRange(EntityManager, _transform, coordinates, 15); + return _transform.InRange(Transform(pointer).Coordinates, coordinates, 15); } else { @@ -145,8 +145,7 @@ namespace Content.Server.Pointing.EntitySystems _popup.PopupEntity(Loc.GetString("pointing-system-try-point-cannot-reach"), player, player); return false; } - - var mapCoordsPointed = coordsPointed.ToMap(EntityManager, _transform); + var mapCoordsPointed = _transform.ToMapCoordinates(coordsPointed); _rotateToFaceSystem.TryFaceCoordinates(player, mapCoordsPointed.Position); var arrow = EntityManager.SpawnEntity("PointingArrow", coordsPointed); @@ -154,7 +153,7 @@ namespace Content.Server.Pointing.EntitySystems if (TryComp(arrow, out var pointing)) { if (TryComp(player, out TransformComponent? xformPlayer)) - pointing.StartPosition = EntityCoordinates.FromMap(arrow, xformPlayer.Coordinates.ToMap(EntityManager, _transform), _transform).Position; + pointing.StartPosition = _transform.ToCoordinates((player, xformPlayer), _transform.ToMapCoordinates(xformPlayer.Coordinates)).Position; pointing.EndTime = _gameTiming.CurTime + PointDuration; diff --git a/Content.Server/Salvage/SalvageSystem.Magnet.cs b/Content.Server/Salvage/SalvageSystem.Magnet.cs index 3fe4baca8b..5e85fdb573 100644 --- a/Content.Server/Salvage/SalvageSystem.Magnet.cs +++ b/Content.Server/Salvage/SalvageSystem.Magnet.cs @@ -8,6 +8,7 @@ using Content.Shared.Radio; using Content.Shared.Salvage.Magnet; using Robust.Server.Maps; using Robust.Shared.Map; +using Robust.Shared.Map.Components; namespace Content.Server.Salvage; @@ -253,7 +254,8 @@ public sealed partial class SalvageSystem var seed = data.Comp.Offered[index]; var offering = GetSalvageOffering(seed); - var salvMap = _mapManager.CreateMap(); + var salvMap = _mapSystem.CreateMap(); + var salvMapXform = Transform(salvMap); // Set values while awaiting asteroid dungeon if relevant so we can't double-take offers. data.Comp.ActiveSeed = seed; @@ -264,8 +266,8 @@ public sealed partial class SalvageSystem switch (offering) { case AsteroidOffering asteroid: - var grid = _mapManager.CreateGrid(salvMap); - await _dungeon.GenerateDungeonAsync(asteroid.DungeonConfig, grid.Owner, grid, Vector2i.Zero, seed); + var grid = _mapManager.CreateGridEntity(salvMap); + await _dungeon.GenerateDungeonAsync(asteroid.DungeonConfig, grid.Owner, grid.Comp, Vector2i.Zero, seed); break; case SalvageOffering wreck: var salvageProto = wreck.SalvageMap; @@ -275,10 +277,10 @@ public sealed partial class SalvageSystem Offset = new Vector2(0, 0) }; - if (!_map.TryLoad(salvMap, salvageProto.MapPath.ToString(), out var roots, opts)) + if (!_map.TryLoad(salvMapXform.MapID, salvageProto.MapPath.ToString(), out _, opts)) { Report(magnet, MagnetChannel, "salvage-system-announcement-spawn-debris-disintegrated"); - _mapManager.DeleteMap(salvMap); + _mapManager.DeleteMap(salvMapXform.MapID); return; } @@ -288,15 +290,14 @@ public sealed partial class SalvageSystem } Box2? bounds = null; - var mapXform = _xformQuery.GetComponent(_mapManager.GetMapEntityId(salvMap)); - if (mapXform.ChildCount == 0) + if (salvMapXform.ChildCount == 0) { Report(magnet.Owner, MagnetChannel, "salvage-system-announcement-spawn-no-debris-available"); return; } - var mapChildren = mapXform.ChildEnumerator; + var mapChildren = salvMapXform.ChildEnumerator; while (mapChildren.MoveNext(out var mapChild)) { @@ -340,19 +341,25 @@ public sealed partial class SalvageSystem if (!TryGetSalvagePlacementLocation(mapId, attachedBounds, bounds!.Value, worldAngle, out var spawnLocation, out var spawnAngle)) { Report(magnet.Owner, MagnetChannel, "salvage-system-announcement-spawn-no-debris-available"); - _mapManager.DeleteMap(salvMap); + _mapManager.DeleteMap(salvMapXform.MapID); return; } + // I have no idea if we want to return on failure or not + // but I assume trying to set the parent with a null value wouldn't have worked out anyways + if (!_mapSystem.TryGetMap(spawnLocation.MapId, out var spawnUid)) + return; + data.Comp.ActiveEntities = null; - mapChildren = mapXform.ChildEnumerator; + mapChildren = salvMapXform.ChildEnumerator; // It worked, move it into position and cleanup values. while (mapChildren.MoveNext(out var mapChild)) { var salvXForm = _xformQuery.GetComponent(mapChild); var localPos = salvXForm.LocalPosition; - _transform.SetParent(mapChild, salvXForm, _mapManager.GetMapEntityId(spawnLocation.MapId)); + + _transform.SetParent(mapChild, salvXForm, spawnUid.Value); _transform.SetWorldPositionRotation(mapChild, spawnLocation.Position + localPos, spawnAngle, salvXForm); data.Comp.ActiveEntities ??= new List(); @@ -371,7 +378,7 @@ public sealed partial class SalvageSystem } Report(magnet.Owner, MagnetChannel, "salvage-system-announcement-arrived", ("timeLeft", data.Comp.ActiveTime.TotalSeconds)); - _mapManager.DeleteMap(salvMap); + _mapManager.DeleteMap(salvMapXform.MapID); data.Comp.Announced = false; diff --git a/Content.Shared/Access/Systems/IdExaminableSystem.cs b/Content.Shared/Access/Systems/IdExaminableSystem.cs index 333272e27a..13359adcba 100644 --- a/Content.Shared/Access/Systems/IdExaminableSystem.cs +++ b/Content.Shared/Access/Systems/IdExaminableSystem.cs @@ -27,7 +27,8 @@ public sealed class IdExaminableSystem : EntitySystem { Act = () => { - var markup = FormattedMessage.FromMarkup(info); + var markup = FormattedMessage.FromMarkupOrThrow(info); + _examineSystem.SendExamineTooltip(args.User, uid, markup, false, false); }, Text = Loc.GetString("id-examinable-component-verb-text"), diff --git a/Content.Shared/Actions/ActionContainerSystem.cs b/Content.Shared/Actions/ActionContainerSystem.cs index 1c5a3ba0d9..1a83cf38e5 100644 --- a/Content.Shared/Actions/ActionContainerSystem.cs +++ b/Content.Shared/Actions/ActionContainerSystem.cs @@ -261,7 +261,7 @@ public sealed class ActionContainerSystem : EntitySystem if (action.Container == null) return; - _transform.DetachParentToNull(actionId, Transform(actionId)); + _transform.DetachEntity(actionId, Transform(actionId)); // Container removal events should have removed the action from the action container. // However, just in case the container was already deleted we will still manually clear the container field diff --git a/Content.Shared/Actions/EntityTargetActionComponent.cs b/Content.Shared/Actions/EntityTargetActionComponent.cs index 9024f42e0e..7217794b23 100644 --- a/Content.Shared/Actions/EntityTargetActionComponent.cs +++ b/Content.Shared/Actions/EntityTargetActionComponent.cs @@ -4,6 +4,9 @@ using Robust.Shared.Serialization; namespace Content.Shared.Actions; +/// +/// Used on action entities to define an action that triggers when targeting an entity. +/// [RegisterComponent, NetworkedComponent] public sealed partial class EntityTargetActionComponent : BaseTargetActionComponent { @@ -16,8 +19,15 @@ public sealed partial class EntityTargetActionComponent : BaseTargetActionCompon [NonSerialized] public EntityTargetActionEvent? Event; + /// + /// Determines which entities are valid targets for this action. + /// + /// No whitelist check when null. [DataField("whitelist")] public EntityWhitelist? Whitelist; + /// + /// Whether this action considers the user as a valid target entity when using this action. + /// [DataField("canTargetSelf")] public bool CanTargetSelf = true; } diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 0e302f1e02..013348eb4f 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -427,7 +427,7 @@ public abstract class SharedActionsSystem : EntitySystem } var entityCoordinatesTarget = GetCoordinates(netCoordinatesTarget); - _rotateToFaceSystem.TryFaceCoordinates(user, entityCoordinatesTarget.ToMapPos(EntityManager, _transformSystem)); + _rotateToFaceSystem.TryFaceCoordinates(user, _transformSystem.ToMapCoordinates(entityCoordinatesTarget).Position); if (!ValidateWorldTarget(user, entityCoordinatesTarget, (actionEnt, worldAction))) return; @@ -533,7 +533,7 @@ public abstract class SharedActionsSystem : EntitySystem if (action.Range <= 0) return true; - return coords.InRange(EntityManager, _transformSystem, Transform(user).Coordinates, action.Range); + return _transformSystem.InRange(coords, Transform(user).Coordinates, action.Range); } return _interactionSystem.InRangeUnobstructed(user, coords, range: action.Range); diff --git a/Content.Shared/Actions/WorldTargetActionComponent.cs b/Content.Shared/Actions/WorldTargetActionComponent.cs index 4974b4478d..8066a64034 100644 --- a/Content.Shared/Actions/WorldTargetActionComponent.cs +++ b/Content.Shared/Actions/WorldTargetActionComponent.cs @@ -3,6 +3,9 @@ using Robust.Shared.Serialization; namespace Content.Shared.Actions; +/// +/// Used on action entities to define an action that triggers when targeting an entity coordinate. +/// [RegisterComponent, NetworkedComponent] public sealed partial class WorldTargetActionComponent : BaseTargetActionComponent { diff --git a/Content.Shared/Audio/AmbientMusicPrototype.cs b/Content.Shared/Audio/AmbientMusicPrototype.cs index 54f70f5728..219c41527d 100644 --- a/Content.Shared/Audio/AmbientMusicPrototype.cs +++ b/Content.Shared/Audio/AmbientMusicPrototype.cs @@ -1,4 +1,5 @@ using Content.Shared.Random; +using Content.Shared.Random.Rules; using Robust.Shared.Audio; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; diff --git a/Content.Shared/Cargo/Components/BankClientComponent.cs b/Content.Shared/Cargo/Components/BankClientComponent.cs new file mode 100644 index 0000000000..4fd7085503 --- /dev/null +++ b/Content.Shared/Cargo/Components/BankClientComponent.cs @@ -0,0 +1,26 @@ +using Content.Shared.Cargo; +using Robust.Shared.GameStates; + +namespace Content.Shared.Cargo.Components; + +/// +/// Makes an entity a client of the station's bank account. +/// When its balance changes it will have raised on it. +/// Other systems can then use this for logic or to update ui states. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedCargoSystem))] +[AutoGenerateComponentState] +public sealed partial class BankClientComponent : Component +{ + /// + /// The balance updated for the last station this entity was a part of. + /// + [DataField, AutoNetworkedField] + public int Balance; +} + +/// +/// Raised on an entity with when the bank's balance is updated. +/// +[ByRefEvent] +public record struct BankBalanceUpdatedEvent(EntityUid Station, int Balance); diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs index 3b75392508..e5d8cc5f59 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs @@ -117,6 +117,7 @@ public sealed class SolutionTransferSystem : EntitySystem transferAmount = FixedPoint2.Min(transferAmount, maxRefill); var transferred = Transfer(args.User, target, targetSoln.Value, uid, ownerSoln.Value, transferAmount); + args.Handled = true; if (transferred > 0) { var toTheBrim = ownerRefill.AvailableVolume == 0; @@ -125,8 +126,6 @@ public sealed class SolutionTransferSystem : EntitySystem : "comp-solution-transfer-fill-normal"; _popup.PopupClient(Loc.GetString(msg, ("owner", args.Target), ("amount", transferred), ("target", uid)), uid, args.User); - - args.Handled = true; return; } } @@ -143,13 +142,11 @@ public sealed class SolutionTransferSystem : EntitySystem transferAmount = FixedPoint2.Min(transferAmount, maxRefill); var transferred = Transfer(args.User, uid, ownerSoln.Value, target, targetSoln.Value, transferAmount); - + args.Handled = true; if (transferred > 0) { var message = Loc.GetString("comp-solution-transfer-transfer-solution", ("amount", transferred), ("target", target)); _popup.PopupClient(message, uid, args.User); - - args.Handled = true; } } } @@ -202,6 +199,9 @@ public sealed class SolutionTransferSystem : EntitySystem var solution = _solution.SplitSolution(source, actualAmount); _solution.AddSolution(target, solution); + var ev = new SolutionTransferredEvent(sourceEntity, targetEntity, user, actualAmount); + RaiseLocalEvent(targetEntity, ref ev); + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} transferred {SharedSolutionContainerSystem.ToPrettyString(solution)} to {ToPrettyString(targetEntity):target}, which now contains {SharedSolutionContainerSystem.ToPrettyString(targetSolution)}"); @@ -225,3 +225,9 @@ public record struct SolutionTransferAttemptEvent(EntityUid From, EntityUid To, CancelReason = reason; } } + +/// +/// Raised on the target entity when a non-zero amount of solution gets transferred. +/// +[ByRefEvent] +public record struct SolutionTransferredEvent(EntityUid From, EntityUid To, EntityUid User, FixedPoint2 Amount); diff --git a/Content.Shared/Clock/ClockComponent.cs b/Content.Shared/Clock/ClockComponent.cs new file mode 100644 index 0000000000..3a1027d813 --- /dev/null +++ b/Content.Shared/Clock/ClockComponent.cs @@ -0,0 +1,42 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Clock; + +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedClockSystem))] +[AutoGenerateComponentState] +public sealed partial class ClockComponent : Component +{ + /// + /// If not null, this time will be permanently shown. + /// + [DataField, AutoNetworkedField] + public TimeSpan? StuckTime; + + /// + /// The format in which time is displayed. + /// + [DataField, AutoNetworkedField] + public ClockType ClockType = ClockType.TwelveHour; + + [DataField] + public string HoursBase = "hours_"; + + [DataField] + public string MinutesBase = "minutes_"; +} + +[Serializable, NetSerializable] +public enum ClockType : byte +{ + TwelveHour, + TwentyFourHour +} + +[Serializable, NetSerializable] +public enum ClockVisualLayers : byte +{ + HourHand, + MinuteHand +} diff --git a/Content.Shared/Clock/GlobalTimeManagerComponent.cs b/Content.Shared/Clock/GlobalTimeManagerComponent.cs new file mode 100644 index 0000000000..764b578b25 --- /dev/null +++ b/Content.Shared/Clock/GlobalTimeManagerComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Clock; + +/// +/// This is used for globally managing the time on-station +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause, Access(typeof(SharedClockSystem))] +public sealed partial class GlobalTimeManagerComponent : Component +{ + /// + /// A fixed random offset, used to fuzz the time between shifts. + /// + [DataField, AutoPausedField, AutoNetworkedField] + public TimeSpan TimeOffset; +} diff --git a/Content.Shared/Clock/SharedClockSystem.cs b/Content.Shared/Clock/SharedClockSystem.cs new file mode 100644 index 0000000000..0a7c1b01b2 --- /dev/null +++ b/Content.Shared/Clock/SharedClockSystem.cs @@ -0,0 +1,66 @@ +using System.Linq; +using Content.Shared.Examine; +using Content.Shared.GameTicking; + +namespace Content.Shared.Clock; + +public abstract class SharedClockSystem : EntitySystem +{ + [Dependency] private readonly SharedGameTicker _ticker = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnExamined); + } + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + args.PushMarkup(Loc.GetString("clock-examine", ("time", GetClockTimeText(ent)))); + } + + public string GetClockTimeText(Entity ent) + { + var time = GetClockTime(ent); + switch (ent.Comp.ClockType) + { + case ClockType.TwelveHour: + return time.ToString(@"h\:mm"); + case ClockType.TwentyFourHour: + return time.ToString(@"hh\:mm"); + default: + throw new ArgumentOutOfRangeException(); + } + } + + private TimeSpan GetGlobalTime() + { + return (EntityQuery().FirstOrDefault()?.TimeOffset ?? TimeSpan.Zero) + _ticker.RoundDuration(); + } + + public TimeSpan GetClockTime(Entity ent) + { + var comp = ent.Comp; + + if (comp.StuckTime != null) + return comp.StuckTime.Value; + + var time = GetGlobalTime(); + + switch (comp.ClockType) + { + case ClockType.TwelveHour: + var adjustedHours = time.Hours % 12; + if (adjustedHours == 0) + adjustedHours = 12; + return new TimeSpan(adjustedHours, time.Minutes, time.Seconds); + case ClockType.TwentyFourHour: + return time; + default: + throw new ArgumentOutOfRangeException(); + } + } +} diff --git a/Content.Shared/Clothing/Components/FoldableClothingComponent.cs b/Content.Shared/Clothing/Components/FoldableClothingComponent.cs index 1a40d1dca1..ffcb52b457 100644 --- a/Content.Shared/Clothing/Components/FoldableClothingComponent.cs +++ b/Content.Shared/Clothing/Components/FoldableClothingComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Humanoid; using Content.Shared.Inventory; using Robust.Shared.GameStates; @@ -30,4 +31,16 @@ public sealed partial class FoldableClothingComponent : Component /// [DataField] public string? FoldedHeldPrefix; + + /// + /// Which layers does this hide when Unfolded? See and + /// + [DataField] + public HashSet UnfoldedHideLayers = new(); + + /// + /// Which layers does this hide when folded? See and + /// + [DataField] + public HashSet FoldedHideLayers = new(); } diff --git a/Content.Shared/Clothing/EntitySystems/FoldableClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/FoldableClothingSystem.cs index be55588ddd..603af4099c 100644 --- a/Content.Shared/Clothing/EntitySystems/FoldableClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/FoldableClothingSystem.cs @@ -47,6 +47,10 @@ public sealed class FoldableClothingSystem : EntitySystem if (ent.Comp.FoldedHeldPrefix != null) _itemSystem.SetHeldPrefix(ent.Owner, ent.Comp.FoldedHeldPrefix, false, itemComp); + + if (TryComp(ent.Owner, out var hideLayerComp)) + hideLayerComp.Slots = ent.Comp.FoldedHideLayers; + } else { @@ -59,6 +63,9 @@ public sealed class FoldableClothingSystem : EntitySystem if (ent.Comp.FoldedHeldPrefix != null) _itemSystem.SetHeldPrefix(ent.Owner, null, false, itemComp); + if (TryComp(ent.Owner, out var hideLayerComp)) + hideLayerComp.Slots = ent.Comp.UnfoldedHideLayers; + } } } diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs index 4f77a271b3..ad94f3b940 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs @@ -170,7 +170,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem if (args.BreakOnMove && !(!args.BreakOnWeightlessMove && _gravity.IsWeightless(args.User, xform: userXform))) { // Whether the user has moved too much from their original position. - if (!userXform.Coordinates.InRange(EntityManager, _transform, doAfter.UserPosition, args.MovementThreshold)) + if (!_transform.InRange(userXform.Coordinates, doAfter.UserPosition, args.MovementThreshold)) return true; // Whether the distance between the user and target(if any) has changed too much. diff --git a/Content.Shared/Extinguisher/SharedFireExtinguisherComponent.cs b/Content.Shared/Extinguisher/SharedFireExtinguisherComponent.cs deleted file mode 100644 index dbb1f6f2c4..0000000000 --- a/Content.Shared/Extinguisher/SharedFireExtinguisherComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Robust.Shared.Audio; -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared.Extinguisher; - -[NetworkedComponent] -public abstract partial class SharedFireExtinguisherComponent : Component -{ - [DataField("refillSound")] public SoundSpecifier RefillSound = new SoundPathSpecifier("/Audio/Effects/refill.ogg"); - - [DataField("hasSafety")] public bool HasSafety = true; - - [DataField("safety")] public bool Safety = true; - - [DataField("safetySound")] - public SoundSpecifier SafetySound { get; private set; } = new SoundPathSpecifier("/Audio/Machines/button.ogg"); -} - - -[Serializable, NetSerializable] -public enum FireExtinguisherVisuals : byte -{ - Safety -} diff --git a/Content.Shared/Fluids/Components/SpraySafetyComponent.cs b/Content.Shared/Fluids/Components/SpraySafetyComponent.cs new file mode 100644 index 0000000000..30827d4fd1 --- /dev/null +++ b/Content.Shared/Fluids/Components/SpraySafetyComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Fluids.Components; + +/// +/// Uses ItemToggle to control safety for a spray item. +/// You can't spray or refill it while safety is on. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SpraySafetySystem))] +public sealed partial class SpraySafetyComponent : Component +{ + /// + /// Popup shown when trying to spray or refill with safety on. + /// + [DataField] + public LocId Popup = "fire-extinguisher-component-safety-on-message"; + + /// + /// Sound to play after refilling. + /// + [DataField] + public SoundSpecifier RefillSound = new SoundPathSpecifier("/Audio/Effects/refill.ogg"); +} diff --git a/Content.Shared/Fluids/Events.cs b/Content.Shared/Fluids/Events.cs index e281de9137..198e888774 100644 --- a/Content.Shared/Fluids/Events.cs +++ b/Content.Shared/Fluids/Events.cs @@ -34,3 +34,15 @@ public sealed partial class AbsorbantDoAfterEvent : DoAfterEvent public override DoAfterEvent Clone() => this; } + +/// +/// Raised when trying to spray something, for example a fire extinguisher. +/// +[ByRefEvent] +public record struct SprayAttemptEvent(EntityUid User, bool Cancelled = false) +{ + public void Cancel() + { + Cancelled = true; + } +} diff --git a/Content.Shared/Fluids/SpraySafetySystem.cs b/Content.Shared/Fluids/SpraySafetySystem.cs new file mode 100644 index 0000000000..82006a995b --- /dev/null +++ b/Content.Shared/Fluids/SpraySafetySystem.cs @@ -0,0 +1,44 @@ +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Fluids.Components; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Popups; +using Robust.Shared.Audio.Systems; + +namespace Content.Shared.Fluids; + +public sealed class SpraySafetySystem : EntitySystem +{ + [Dependency] private readonly ItemToggleSystem _toggle = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTransferAttempt); + SubscribeLocalEvent(OnTransferred); + SubscribeLocalEvent(OnSprayAttempt); + } + + private void OnTransferAttempt(Entity ent, ref SolutionTransferAttemptEvent args) + { + var (uid, comp) = ent; + if (uid == args.To && !_toggle.IsActivated(uid)) + args.Cancel(Loc.GetString(comp.Popup)); + } + + private void OnTransferred(Entity ent, ref SolutionTransferredEvent args) + { + _audio.PlayPredicted(ent.Comp.RefillSound, ent, args.User); + } + + private void OnSprayAttempt(Entity ent, ref SprayAttemptEvent args) + { + if (!_toggle.IsActivated(ent.Owner)) + { + _popup.PopupEntity(Loc.GetString(ent.Comp.Popup), ent, args.User); + args.Cancel(); + } + } +} diff --git a/Content.Shared/Follower/FollowerSystem.cs b/Content.Shared/Follower/FollowerSystem.cs index 8027ee449c..8c35617e2c 100644 --- a/Content.Shared/Follower/FollowerSystem.cs +++ b/Content.Shared/Follower/FollowerSystem.cs @@ -227,7 +227,7 @@ public sealed class FollowerSystem : EntitySystem if (_netMan.IsClient) { - _transform.DetachParentToNull(uid, xform); + _transform.DetachEntity(uid, xform); return; } diff --git a/Content.Shared/Hands/Components/HandsComponent.cs b/Content.Shared/Hands/Components/HandsComponent.cs index 826c496b41..a1d470b211 100644 --- a/Content.Shared/Hands/Components/HandsComponent.cs +++ b/Content.Shared/Hands/Components/HandsComponent.cs @@ -43,7 +43,7 @@ public sealed partial class HandsComponent : Component /// [DataField] [ViewVariables(VVAccess.ReadWrite)] - public float BaseThrowspeed { get; set; } = 10f; + public float BaseThrowspeed { get; set; } = 11f; /// /// Distance after which longer throw targets stop increasing throw impulse. diff --git a/Content.Shared/Interaction/SmartEquipSystem.cs b/Content.Shared/Interaction/SmartEquipSystem.cs index bba294db28..4feb0445f8 100644 --- a/Content.Shared/Interaction/SmartEquipSystem.cs +++ b/Content.Shared/Interaction/SmartEquipSystem.cs @@ -62,21 +62,22 @@ public sealed class SmartEquipSystem : EntitySystem if (playerSession.AttachedEntity is not { Valid: true } uid || !Exists(uid)) return; - if (!_actionBlocker.CanInteract(uid, null)) - return; - // early out if we don't have any hands or a valid inventory slot if (!TryComp(uid, out var hands) || hands.ActiveHand == null) return; + var handItem = hands.ActiveHand.HeldEntity; + + // can the user interact, and is the item interactable? e.g. virtual items + if (!_actionBlocker.CanInteract(uid, handItem)) + return; + if (!TryComp(uid, out var inventory) || !_inventory.HasSlot(uid, equipmentSlot, inventory)) { _popup.PopupClient(Loc.GetString("smart-equip-missing-equipment-slot", ("slotName", equipmentSlot)), uid, uid); return; } - var handItem = hands.ActiveHand.HeldEntity; - // early out if we have an item and cant drop it at all if (handItem != null && !_hands.CanDropHeld(uid, hands.ActiveHand)) { diff --git a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs index 4a5894d895..8b9c052c8d 100644 --- a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs +++ b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.Hands; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Popups; @@ -43,6 +44,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem SubscribeLocalEvent(OnBeingUnequippedAttempt); SubscribeLocalEvent(OnBeforeRangedInteract); + SubscribeLocalEvent(OnGettingInteractedWithAttemptEvent); } /// @@ -72,6 +74,12 @@ public abstract class SharedVirtualItemSystem : EntitySystem args.Handled = true; } + private void OnGettingInteractedWithAttemptEvent(Entity ent, ref GettingInteractedWithAttemptEvent args) + { + // No interactions with a virtual item, please. + args.Cancelled = true; + } + #region Hands /// @@ -244,7 +252,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem if (TerminatingOrDeleted(item)) return; - _transformSystem.DetachParentToNull(item, Transform(item)); + _transformSystem.DetachEntity(item, Transform(item)); if (_netManager.IsServer) QueueDel(item); } diff --git a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs index 8d2c4dcfeb..35fa501398 100644 --- a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs +++ b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs @@ -1,5 +1,5 @@ +using System.Diagnostics.CodeAnalysis; using Content.Shared.Mind; -using Content.Shared.Objectives; using Content.Shared.Objectives.Components; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -40,7 +40,7 @@ public abstract class SharedObjectivesSystem : EntitySystem if (comp.Unique) { var proto = _metaQuery.GetComponent(uid).EntityPrototype?.ID; - foreach (var objective in mind.AllObjectives) + foreach (var objective in mind.Objectives) { if (_metaQuery.GetComponent(objective).EntityPrototype?.ID == proto) return false; @@ -92,7 +92,18 @@ public abstract class SharedObjectivesSystem : EntitySystem } /// - /// Get the title, description, icon and progress of an objective using . + /// Spawns and assigns an objective for a mind. + /// The objective is not added to the mind's objectives, mind system does that in TryAddObjective. + /// If the objective could not be assigned the objective is deleted and false is returned. + /// + public bool TryCreateObjective(Entity mind, EntProtoId proto, [NotNullWhen(true)] out EntityUid? objective) + { + objective = TryCreateObjective(mind.Owner, mind.Comp, proto); + return objective != null; + } + + /// + /// Get the title, description, icon and progress of an objective using . /// If any of them are null it is logged and null is returned. /// /// ID of the condition entity diff --git a/Content.Shared/Random/Helpers/SharedRandomExtensions.cs b/Content.Shared/Random/Helpers/SharedRandomExtensions.cs index 0b618a262d..376e91743d 100644 --- a/Content.Shared/Random/Helpers/SharedRandomExtensions.cs +++ b/Content.Shared/Random/Helpers/SharedRandomExtensions.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Dataset; using Content.Shared.FixedPoint; @@ -87,6 +88,26 @@ namespace Content.Shared.Random.Helpers throw new InvalidOperationException("Invalid weighted pick"); } + public static T PickAndTake(this IRobustRandom random, Dictionary weights) + where T : notnull + { + var pick = Pick(random, weights); + weights.Remove(pick); + return pick; + } + + public static bool TryPickAndTake(this IRobustRandom random, Dictionary weights, [NotNullWhen(true)] out T? pick) + where T : notnull + { + if (weights.Count == 0) + { + pick = default; + return false; + } + pick = PickAndTake(random, weights); + return true; + } + public static (string reagent, FixedPoint2 quantity) Pick(this WeightedRandomFillSolutionPrototype prototype, IRobustRandom? random = null) { var randomFill = prototype.PickRandomFill(random); diff --git a/Content.Shared/Random/Rules/AlwaysTrue.cs b/Content.Shared/Random/Rules/AlwaysTrue.cs new file mode 100644 index 0000000000..98b81fae79 --- /dev/null +++ b/Content.Shared/Random/Rules/AlwaysTrue.cs @@ -0,0 +1,12 @@ +namespace Content.Shared.Random.Rules; + +/// +/// Always returns true. Used for fallbacks. +/// +public sealed partial class AlwaysTrueRule : RulesRule +{ + public override bool Check(EntityManager entManager, EntityUid uid) + { + return !Inverted; + } +} diff --git a/Content.Shared/Random/Rules/GridInRange.cs b/Content.Shared/Random/Rules/GridInRange.cs new file mode 100644 index 0000000000..8cbbef1cdc --- /dev/null +++ b/Content.Shared/Random/Rules/GridInRange.cs @@ -0,0 +1,39 @@ +using System.Numerics; +using Robust.Shared.Map; + +namespace Content.Shared.Random.Rules; + +/// +/// Returns true if on a grid or in range of one. +/// +public sealed partial class GridInRangeRule : RulesRule +{ + [DataField] + public float Range = 10f; + + public override bool Check(EntityManager entManager, EntityUid uid) + { + if (!entManager.TryGetComponent(uid, out TransformComponent? xform)) + { + return false; + } + + if (xform.GridUid != null) + { + return !Inverted; + } + + var transform = entManager.System(); + var mapManager = IoCManager.Resolve(); + + var worldPos = transform.GetWorldPosition(xform); + var gridRange = new Vector2(Range, Range); + + foreach (var _ in mapManager.FindGridsIntersecting(xform.MapID, new Box2(worldPos - gridRange, worldPos + gridRange))) + { + return !Inverted; + } + + return false; + } +} diff --git a/Content.Shared/Random/Rules/InSpace.cs b/Content.Shared/Random/Rules/InSpace.cs new file mode 100644 index 0000000000..8163e1f6be --- /dev/null +++ b/Content.Shared/Random/Rules/InSpace.cs @@ -0,0 +1,18 @@ +namespace Content.Shared.Random.Rules; + +/// +/// Returns true if the attached entity is in space. +/// +public sealed partial class InSpaceRule : RulesRule +{ + public override bool Check(EntityManager entManager, EntityUid uid) + { + if (!entManager.TryGetComponent(uid, out TransformComponent? xform) || + xform.GridUid != null) + { + return Inverted; + } + + return !Inverted; + } +} diff --git a/Content.Shared/Random/Rules/NearbyAccess.cs b/Content.Shared/Random/Rules/NearbyAccess.cs new file mode 100644 index 0000000000..2a8ad077c6 --- /dev/null +++ b/Content.Shared/Random/Rules/NearbyAccess.cs @@ -0,0 +1,77 @@ +using Content.Shared.Access; +using Content.Shared.Access.Components; +using Content.Shared.Access.Systems; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Random.Rules; + +/// +/// Checks for an entity nearby with the specified access. +/// +public sealed partial class NearbyAccessRule : RulesRule +{ + // This exists because of door electronics contained inside doors. + /// + /// Does the access entity need to be anchored. + /// + [DataField] + public bool Anchored = true; + + /// + /// Count of entities that need to be nearby. + /// + [DataField] + public int Count = 1; + + [DataField(required: true)] + public List> Access = new(); + + [DataField] + public float Range = 10f; + + public override bool Check(EntityManager entManager, EntityUid uid) + { + var xformQuery = entManager.GetEntityQuery(); + + if (!xformQuery.TryGetComponent(uid, out var xform) || + xform.MapUid == null) + { + return false; + } + + var transform = entManager.System(); + var lookup = entManager.System(); + var reader = entManager.System(); + + var found = false; + var worldPos = transform.GetWorldPosition(xform, xformQuery); + var count = 0; + + // TODO: Update this when we get the callback version + var entities = new HashSet>(); + lookup.GetEntitiesInRange(xform.MapID, worldPos, Range, entities); + foreach (var comp in entities) + { + if (!reader.AreAccessTagsAllowed(Access, comp) || + Anchored && + (!xformQuery.TryGetComponent(comp, out var compXform) || + !compXform.Anchored)) + { + continue; + } + + count++; + + if (count < Count) + continue; + + found = true; + break; + } + + if (!found) + return Inverted; + + return !Inverted; + } +} diff --git a/Content.Shared/Random/Rules/NearbyComponents.cs b/Content.Shared/Random/Rules/NearbyComponents.cs new file mode 100644 index 0000000000..13108e88d6 --- /dev/null +++ b/Content.Shared/Random/Rules/NearbyComponents.cs @@ -0,0 +1,71 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Random.Rules; + +public sealed partial class NearbyComponentsRule : RulesRule +{ + /// + /// Does the entity need to be anchored. + /// + [DataField] + public bool Anchored; + + [DataField] + public int Count; + + [DataField(required: true)] + public ComponentRegistry Components = default!; + + [DataField] + public float Range = 10f; + + public override bool Check(EntityManager entManager, EntityUid uid) + { + var inRange = new HashSet>(); + var xformQuery = entManager.GetEntityQuery(); + + if (!xformQuery.TryGetComponent(uid, out var xform) || + xform.MapUid == null) + { + return false; + } + + var transform = entManager.System(); + var lookup = entManager.System(); + + var found = false; + var worldPos = transform.GetWorldPosition(xform); + var count = 0; + + foreach (var compType in Components.Values) + { + inRange.Clear(); + lookup.GetEntitiesInRange(compType.Component.GetType(), xform.MapID, worldPos, Range, inRange); + foreach (var comp in inRange) + { + if (Anchored && + (!xformQuery.TryGetComponent(comp, out var compXform) || + !compXform.Anchored)) + { + continue; + } + + count++; + + if (count < Count) + continue; + + found = true; + break; + } + + if (found) + break; + } + + if (!found) + return Inverted; + + return !Inverted; + } +} diff --git a/Content.Shared/Random/Rules/NearbyEntities.cs b/Content.Shared/Random/Rules/NearbyEntities.cs new file mode 100644 index 0000000000..0754750bc4 --- /dev/null +++ b/Content.Shared/Random/Rules/NearbyEntities.cs @@ -0,0 +1,58 @@ +using Content.Shared.Whitelist; + +namespace Content.Shared.Random.Rules; + +/// +/// Checks for entities matching the whitelist in range. +/// This is more expensive than so prefer that! +/// +public sealed partial class NearbyEntitiesRule : RulesRule +{ + /// + /// How many of the entity need to be nearby. + /// + [DataField] + public int Count = 1; + + [DataField(required: true)] + public EntityWhitelist Whitelist = new(); + + [DataField] + public float Range = 10f; + + public override bool Check(EntityManager entManager, EntityUid uid) + { + if (!entManager.TryGetComponent(uid, out TransformComponent? xform) || + xform.MapUid == null) + { + return false; + } + + var transform = entManager.System(); + var lookup = entManager.System(); + var whitelistSystem = entManager.System(); + + var found = false; + var worldPos = transform.GetWorldPosition(xform); + var count = 0; + + foreach (var ent in lookup.GetEntitiesInRange(xform.MapID, worldPos, Range)) + { + if (whitelistSystem.IsWhitelistFail(Whitelist, ent)) + continue; + + count++; + + if (count < Count) + continue; + + found = true; + break; + } + + if (!found) + return Inverted; + + return !Inverted; + } +} diff --git a/Content.Shared/Random/Rules/NearbyTilesPercent.cs b/Content.Shared/Random/Rules/NearbyTilesPercent.cs new file mode 100644 index 0000000000..465ac8dc5c --- /dev/null +++ b/Content.Shared/Random/Rules/NearbyTilesPercent.cs @@ -0,0 +1,79 @@ +using Content.Shared.Maps; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Physics.Components; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Random.Rules; + +public sealed partial class NearbyTilesPercentRule : RulesRule +{ + /// + /// If there are anchored entities on the tile do we ignore the tile. + /// + [DataField] + public bool IgnoreAnchored; + + [DataField(required: true)] + public float Percent; + + [DataField(required: true)] + public List> Tiles = new(); + + [DataField] + public float Range = 10f; + + public override bool Check(EntityManager entManager, EntityUid uid) + { + if (!entManager.TryGetComponent(uid, out TransformComponent? xform) || + !entManager.TryGetComponent(xform.GridUid, out var grid)) + { + return false; + } + + var transform = entManager.System(); + var tileDef = IoCManager.Resolve(); + + var physicsQuery = entManager.GetEntityQuery(); + var tileCount = 0; + var matchingTileCount = 0; + + foreach (var tile in grid.GetTilesIntersecting(new Circle(transform.GetWorldPosition(xform), + Range))) + { + // Only consider collidable anchored (for reasons some subfloor stuff has physics but non-collidable) + if (IgnoreAnchored) + { + var gridEnum = grid.GetAnchoredEntitiesEnumerator(tile.GridIndices); + var found = false; + + while (gridEnum.MoveNext(out var ancUid)) + { + if (!physicsQuery.TryGetComponent(ancUid, out var physics) || + !physics.CanCollide) + { + continue; + } + + found = true; + break; + } + + if (found) + continue; + } + + tileCount++; + + if (!Tiles.Contains(tileDef[tile.Tile.TypeId].ID)) + continue; + + matchingTileCount++; + } + + if (tileCount == 0 || matchingTileCount / (float) tileCount < Percent) + return Inverted; + + return !Inverted; + } +} diff --git a/Content.Shared/Random/Rules/OnMapGrid.cs b/Content.Shared/Random/Rules/OnMapGrid.cs new file mode 100644 index 0000000000..bea841872e --- /dev/null +++ b/Content.Shared/Random/Rules/OnMapGrid.cs @@ -0,0 +1,19 @@ +namespace Content.Shared.Random.Rules; + +/// +/// Returns true if griduid and mapuid match (AKA on 'planet'). +/// +public sealed partial class OnMapGridRule : RulesRule +{ + public override bool Check(EntityManager entManager, EntityUid uid) + { + if (!entManager.TryGetComponent(uid, out TransformComponent? xform) || + xform.GridUid != xform.MapUid || + xform.MapUid == null) + { + return Inverted; + } + + return !Inverted; + } +} diff --git a/Content.Shared/Random/Rules/RulesSystem.cs b/Content.Shared/Random/Rules/RulesSystem.cs new file mode 100644 index 0000000000..1957beab51 --- /dev/null +++ b/Content.Shared/Random/Rules/RulesSystem.cs @@ -0,0 +1,39 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Random.Rules; + +/// +/// Rules-based item selection. Can be used for any sort of conditional selection +/// Every single condition needs to be true for this to be selected. +/// e.g. "choose maintenance audio if 90% of tiles nearby are maintenance tiles" +/// +[Prototype("rules")] +public sealed partial class RulesPrototype : IPrototype +{ + [IdDataField] public string ID { get; } = string.Empty; + + [DataField("rules", required: true)] + public List Rules = new(); +} + +[ImplicitDataDefinitionForInheritors] +public abstract partial class RulesRule +{ + [DataField] + public bool Inverted; + public abstract bool Check(EntityManager entManager, EntityUid uid); +} + +public sealed class RulesSystem : EntitySystem +{ + public bool IsTrue(EntityUid uid, RulesPrototype rules) + { + foreach (var rule in rules.Rules) + { + if (!rule.Check(EntityManager, uid)) + return false; + } + + return true; + } +} diff --git a/Content.Shared/Random/RulesPrototype.cs b/Content.Shared/Random/RulesPrototype.cs deleted file mode 100644 index 20961af8e7..0000000000 --- a/Content.Shared/Random/RulesPrototype.cs +++ /dev/null @@ -1,141 +0,0 @@ -using Content.Shared.Access; -using Content.Shared.Maps; -using Content.Shared.Whitelist; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Shared.Random; - -/// -/// Rules-based item selection. Can be used for any sort of conditional selection -/// Every single condition needs to be true for this to be selected. -/// e.g. "choose maintenance audio if 90% of tiles nearby are maintenance tiles" -/// -[Prototype("rules")] -public sealed partial class RulesPrototype : IPrototype -{ - [IdDataField] public string ID { get; } = string.Empty; - - [DataField("rules", required: true)] - public List Rules = new(); -} - -[ImplicitDataDefinitionForInheritors] -public abstract partial class RulesRule -{ - -} - -/// -/// Returns true if the attached entity is in space. -/// -public sealed partial class InSpaceRule : RulesRule -{ - -} - -/// -/// Checks for entities matching the whitelist in range. -/// This is more expensive than so prefer that! -/// -public sealed partial class NearbyEntitiesRule : RulesRule -{ - /// - /// How many of the entity need to be nearby. - /// - [DataField("count")] - public int Count = 1; - - [DataField("whitelist", required: true)] - public EntityWhitelist Whitelist = new(); - - [DataField("range")] - public float Range = 10f; -} - -public sealed partial class NearbyTilesPercentRule : RulesRule -{ - /// - /// If there are anchored entities on the tile do we ignore the tile. - /// - [DataField("ignoreAnchored")] public bool IgnoreAnchored; - - [DataField("percent", required: true)] - public float Percent; - - [DataField("tiles", required: true, customTypeSerializer:typeof(PrototypeIdListSerializer))] - public List Tiles = new(); - - [DataField("range")] - public float Range = 10f; -} - -/// -/// Always returns true. Used for fallbacks. -/// -public sealed partial class AlwaysTrueRule : RulesRule -{ - -} - -/// -/// Returns true if on a grid or in range of one. -/// -public sealed partial class GridInRangeRule : RulesRule -{ - [DataField("range")] - public float Range = 10f; - - [DataField("inverted")] - public bool Inverted = false; -} - -/// -/// Returns true if griduid and mapuid match (AKA on 'planet'). -/// -public sealed partial class OnMapGridRule : RulesRule -{ - -} - -/// -/// Checks for an entity nearby with the specified access. -/// -public sealed partial class NearbyAccessRule : RulesRule -{ - // This exists because of doorelectronics contained inside doors. - /// - /// Does the access entity need to be anchored. - /// - [DataField("anchored")] - public bool Anchored = true; - - /// - /// Count of entities that need to be nearby. - /// - [DataField("count")] - public int Count = 1; - - [DataField("access", required: true)] - public List> Access = new(); - - [DataField("range")] - public float Range = 10f; -} - -public sealed partial class NearbyComponentsRule : RulesRule -{ - /// - /// Does the entity need to be anchored. - /// - [DataField("anchored")] - public bool Anchored; - - [DataField("count")] public int Count; - - [DataField("components", required: true)] - public ComponentRegistry Components = default!; - - [DataField("range")] - public float Range = 10f; -} diff --git a/Content.Shared/Random/RulesSystem.cs b/Content.Shared/Random/RulesSystem.cs deleted file mode 100644 index 58d67c268e..0000000000 --- a/Content.Shared/Random/RulesSystem.cs +++ /dev/null @@ -1,247 +0,0 @@ -using System.Numerics; -using Content.Shared.Access.Components; -using Content.Shared.Access.Systems; -using Content.Shared.Whitelist; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Physics.Components; - -namespace Content.Shared.Random; - -public sealed class RulesSystem : EntitySystem -{ - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly ITileDefinitionManager _tileDef = default!; - [Dependency] private readonly AccessReaderSystem _reader = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; - public bool IsTrue(EntityUid uid, RulesPrototype rules) - { - var inRange = new HashSet>(); - foreach (var rule in rules.Rules) - { - switch (rule) - { - case AlwaysTrueRule: - break; - case GridInRangeRule griddy: - { - if (!TryComp(uid, out TransformComponent? xform)) - { - return false; - } - - if (xform.GridUid != null) - { - return !griddy.Inverted; - } - - var worldPos = _transform.GetWorldPosition(xform); - var gridRange = new Vector2(griddy.Range, griddy.Range); - - foreach (var _ in _mapManager.FindGridsIntersecting( - xform.MapID, - new Box2(worldPos - gridRange, worldPos + gridRange))) - { - return !griddy.Inverted; - } - - break; - } - case InSpaceRule: - { - if (!TryComp(uid, out TransformComponent? xform) || - xform.GridUid != null) - { - return false; - } - - break; - } - case NearbyAccessRule access: - { - var xformQuery = GetEntityQuery(); - - if (!xformQuery.TryGetComponent(uid, out var xform) || - xform.MapUid == null) - { - return false; - } - - var found = false; - var worldPos = _transform.GetWorldPosition(xform, xformQuery); - var count = 0; - - // TODO: Update this when we get the callback version - var entities = new HashSet>(); - _lookup.GetEntitiesInRange(xform.MapID, worldPos, access.Range, entities); - foreach (var comp in entities) - { - if (!_reader.AreAccessTagsAllowed(access.Access, comp) || - access.Anchored && - (!xformQuery.TryGetComponent(comp, out var compXform) || - !compXform.Anchored)) - { - continue; - } - - count++; - - if (count < access.Count) - continue; - - found = true; - break; - } - - if (!found) - return false; - - break; - } - case NearbyComponentsRule nearbyComps: - { - var xformQuery = GetEntityQuery(); - - if (!xformQuery.TryGetComponent(uid, out var xform) || - xform.MapUid == null) - { - return false; - } - - var found = false; - var worldPos = _transform.GetWorldPosition(xform); - var count = 0; - - foreach (var compType in nearbyComps.Components.Values) - { - inRange.Clear(); - _lookup.GetEntitiesInRange(compType.Component.GetType(), xform.MapID, worldPos, nearbyComps.Range, inRange); - foreach (var comp in inRange) - { - if (nearbyComps.Anchored && - (!xformQuery.TryGetComponent(comp, out var compXform) || - !compXform.Anchored)) - { - continue; - } - - count++; - - if (count < nearbyComps.Count) - continue; - - found = true; - break; - } - - if (found) - break; - } - - if (!found) - return false; - - break; - } - case NearbyEntitiesRule entity: - { - if (!TryComp(uid, out TransformComponent? xform) || - xform.MapUid == null) - { - return false; - } - - var found = false; - var worldPos = _transform.GetWorldPosition(xform); - var count = 0; - - foreach (var ent in _lookup.GetEntitiesInRange(xform.MapID, worldPos, entity.Range)) - { - if (_whitelistSystem.IsWhitelistFail(entity.Whitelist, ent)) - continue; - - count++; - - if (count < entity.Count) - continue; - - found = true; - break; - } - - if (!found) - return false; - - break; - } - case NearbyTilesPercentRule tiles: - { - if (!TryComp(uid, out TransformComponent? xform) || - !TryComp(xform.GridUid, out var grid)) - { - return false; - } - - var physicsQuery = GetEntityQuery(); - var tileCount = 0; - var matchingTileCount = 0; - - foreach (var tile in grid.GetTilesIntersecting(new Circle(_transform.GetWorldPosition(xform), - tiles.Range))) - { - // Only consider collidable anchored (for reasons some subfloor stuff has physics but non-collidable) - if (tiles.IgnoreAnchored) - { - var gridEnum = grid.GetAnchoredEntitiesEnumerator(tile.GridIndices); - var found = false; - - while (gridEnum.MoveNext(out var ancUid)) - { - if (!physicsQuery.TryGetComponent(ancUid, out var physics) || - !physics.CanCollide) - { - continue; - } - - found = true; - break; - } - - if (found) - continue; - } - - tileCount++; - - if (!tiles.Tiles.Contains(_tileDef[tile.Tile.TypeId].ID)) - continue; - - matchingTileCount++; - } - - if (tileCount == 0 || matchingTileCount / (float) tileCount < tiles.Percent) - return false; - - break; - } - case OnMapGridRule: - { - if (!TryComp(uid, out TransformComponent? xform) || - xform.GridUid != xform.MapUid || - xform.MapUid == null) - { - return false; - } - - break; - } - default: - throw new NotImplementedException(); - } - } - - return true; - } -} diff --git a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs index cfa082a9ee..7ae821d8d9 100644 --- a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs @@ -4,106 +4,109 @@ using Content.Shared.Whitelist; using JetBrains.Annotations; using Robust.Shared.Containers; -namespace Content.Shared.Storage.EntitySystems +namespace Content.Shared.Storage.EntitySystems; + +/// +/// ItemMapperSystem is a system that on each initialization, insertion, removal of an entity from +/// given (with appropriate storage attached) will check each stored item to see +/// if its tags/component, and overall quantity match . +/// +[UsedImplicitly] +public abstract class SharedItemMapperSystem : EntitySystem { - /// - /// ItemMapperSystem is a system that on each initialization, insertion, removal of an entity from - /// given (with appropriate storage attached) will check each stored item to see - /// if its tags/component, and overall quantity match . - /// - [UsedImplicitly] - public abstract class SharedItemMapperSystem : EntitySystem + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + + /// + public override void Initialize() { - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + base.Initialize(); + SubscribeLocalEvent(InitLayers); + SubscribeLocalEvent(MapperEntityInserted); + SubscribeLocalEvent(MapperEntityRemoved); + } - /// - public override void Initialize() + private void InitLayers(EntityUid uid, ItemMapperComponent component, ComponentInit args) + { + foreach (var (layerName, val) in component.MapLayers) { - base.Initialize(); - SubscribeLocalEvent(InitLayers); - SubscribeLocalEvent(MapperEntityInserted); - SubscribeLocalEvent(MapperEntityRemoved); + val.Layer = layerName; } - private void InitLayers(EntityUid uid, ItemMapperComponent component, ComponentInit args) + if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent)) { - foreach (var (layerName, val) in component.MapLayers) - { - val.Layer = layerName; - } - - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent)) - { - var list = new List(component.MapLayers.Keys); - _appearance.SetData(uid, StorageMapVisuals.InitLayers, new ShowLayerData(list), appearanceComponent); - } - - // Ensure appearance is correct with current contained entities. - UpdateAppearance(uid, component); + var list = new List(component.MapLayers.Keys); + _appearance.SetData(uid, StorageMapVisuals.InitLayers, new ShowLayerData(list), appearanceComponent); } - private void MapperEntityRemoved(EntityUid uid, ItemMapperComponent itemMapper, - EntRemovedFromContainerMessage args) + // Ensure appearance is correct with current contained entities. + UpdateAppearance(uid, component); + } + + private void MapperEntityRemoved(EntityUid uid, ItemMapperComponent itemMapper, EntRemovedFromContainerMessage args) + { + if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID)) + return; + + UpdateAppearance(uid, itemMapper); + } + + private void MapperEntityInserted(EntityUid uid, + ItemMapperComponent itemMapper, + EntInsertedIntoContainerMessage args) + { + if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID)) + return; + + UpdateAppearance(uid, itemMapper); + } + + private void UpdateAppearance(EntityUid uid, ItemMapperComponent? itemMapper = null) + { + if (!Resolve(uid, ref itemMapper)) + return; + + if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent) + && TryGetLayers(uid, itemMapper, out var containedLayers)) { - if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID)) - return; - - UpdateAppearance(uid, itemMapper); - } - - private void MapperEntityInserted(EntityUid uid, ItemMapperComponent itemMapper, - EntInsertedIntoContainerMessage args) - { - if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID)) - return; - - UpdateAppearance(uid, itemMapper); - } - - private void UpdateAppearance(EntityUid uid, ItemMapperComponent? itemMapper = null) - { - if(!Resolve(uid, ref itemMapper)) - return; - - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent) - && TryGetLayers(uid, itemMapper, out var containedLayers)) - { - _appearance.SetData(uid, StorageMapVisuals.LayerChanged, new ShowLayerData(containedLayers), appearanceComponent); - } - } - - /// - /// Method that iterates over storage of the entity in and sets according to - /// definition. It will have O(n*m) time behavior (n - number of entities in container, and m - number of - /// definitions in . - /// - /// EntityUid used to search the storage - /// component that contains definition used to map whitelist in - /// mapLayers to string. - /// - /// list of layers that should be visible - /// false if msg.Container.Owner is not a storage, true otherwise. - private bool TryGetLayers(EntityUid uid, - ItemMapperComponent itemMapper, - out List showLayers) - { - var containedLayers = _container.GetAllContainers(uid) - .Where(c => itemMapper.ContainerWhitelist?.Contains(c.ID) ?? true).SelectMany(cont => cont.ContainedEntities).ToArray(); - - var list = new List(); - foreach (var mapLayerData in itemMapper.MapLayers.Values) - { - var count = containedLayers.Count(ent => _whitelistSystem.IsWhitelistPass(mapLayerData.Whitelist, ent)); - if (count >= mapLayerData.MinCount && count <= mapLayerData.MaxCount) - { - list.Add(mapLayerData.Layer); - } - } - - showLayers = list; - return true; + _appearance.SetData(uid, + StorageMapVisuals.LayerChanged, + new ShowLayerData(containedLayers), + appearanceComponent); } } + + /// + /// Method that iterates over storage of the entity in and sets + /// according to definition. It will have O(n*m) time behavior + /// (n - number of entities in container, and m - number of definitions in ). + /// + /// EntityUid used to search the storage + /// component that contains definition used to map + /// Whitelist in to string. + /// + /// list of layers that should be visible + /// false if msg.Container.Owner is not a storage, true otherwise. + private bool TryGetLayers(EntityUid uid, ItemMapperComponent itemMapper, out List showLayers) + { + var containedLayers = _container.GetAllContainers(uid) + .Where(c => itemMapper.ContainerWhitelist?.Contains(c.ID) ?? true) + .SelectMany(cont => cont.ContainedEntities) + .ToArray(); + + var list = new List(); + foreach (var mapLayerData in itemMapper.MapLayers.Values) + { + var count = containedLayers.Count(ent => _whitelistSystem.IsWhitelistPassOrNull(mapLayerData.Whitelist, + ent)); + if (count >= mapLayerData.MinCount && count <= mapLayerData.MaxCount) + { + list.Add(mapLayerData.Layer); + } + } + + showLayers = list; + return true; + } } diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs index 56bbf4c2bf..549473278e 100644 --- a/Content.Shared/Throwing/ThrowingSystem.cs +++ b/Content.Shared/Throwing/ThrowingSystem.cs @@ -26,11 +26,6 @@ public sealed class ThrowingSystem : EntitySystem public const float PushbackDefault = 2f; - /// - /// The minimum amount of time an entity needs to be thrown before the timer can be run. - /// Anything below this threshold never enters the air. - /// - public const float MinFlyTime = 0.15f; public const float FlyTimePercentage = 0.8f; private float _frictionModifier; @@ -168,9 +163,6 @@ public sealed class ThrowingSystem : EntitySystem var flyTime = direction.Length() / baseThrowSpeed; if (compensateFriction) flyTime *= FlyTimePercentage; - - if (flyTime < MinFlyTime) - flyTime = 0f; comp.ThrownTime = _gameTiming.CurTime; comp.LandTime = comp.ThrownTime + TimeSpan.FromSeconds(flyTime); comp.PlayLandSound = playSound; diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs index adae26a223..d6f45ba77d 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs @@ -108,7 +108,7 @@ public abstract partial class SharedGunSystem else { // Similar to below just due to prediction. - TransformSystem.DetachParentToNull(chamberEnt.Value, Transform(chamberEnt.Value)); + TransformSystem.DetachEntity(chamberEnt.Value, Transform(chamberEnt.Value)); } } diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 90c0216946..0b965b1970 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,119 +1,4 @@ Entries: -- author: IProduceWidgets - changes: - - message: New map Oasis. - type: Add - id: 6409 - time: '2024-04-21T05:59:12.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25736 -- author: KittenColony - changes: - - message: Added a new lizard snout, featuring a split muzzle and snoot that can - be coloured independently - type: Add - id: 6410 - time: '2024-04-21T06:18:33.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27143 -- author: SpeltIncorrectyl - changes: - - message: Security Officers, Head of Security, and Warden can now choose to start - with a security webbing instead of a belt in their loadout menu. - type: Add - id: 6411 - time: '2024-04-21T06:19:20.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27189 -- author: ElectroJr - changes: - - message: Fixed the nukie station not getting properly initialized. - type: Fix - id: 6412 - time: '2024-04-21T06:52:45.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27201 -- author: Blackern5000 - changes: - - message: '"Experienced" passengers can now wear a rainbow jumpsuit using loadouts.' - type: Add - id: 6413 - time: '2024-04-21T11:53:08.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27211 -- author: PJB3005 - changes: - - message: The item status menu has been moved to the side of the hands. There is - now one for each hand. - type: Add - - message: The item status menu fits with the rest of the HUD theme now. - type: Add - - message: Improved, fixed and otherwise added a bunch of item status menus. - type: Add - id: 6414 - time: '2024-04-21T13:16:23.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/22986 -- author: FairlySadPanda - changes: - - message: Xenoarchaeology Traversal Distorters have been rolled into the Artifact - Analyzers to make artifact gameplay a bit more controlled. - type: Tweak - - message: The T2 Abnormal Artifact Manipulation tech has been made cheaper to compensate - for losing an unlock. - type: Tweak - - message: The Xenoarchaeology guidebook entry has been rewritten to be more useful - and comprehensive for newbies. - type: Tweak - id: 6415 - time: '2024-04-21T16:09:26.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26545 -- author: deltanedas - changes: - - message: Flaming mice no longer completely engulf people they touch. - type: Tweak - id: 6416 - time: '2024-04-22T08:42:26.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27202 -- author: Weax - changes: - - message: The CLF3 reaction now requires heating first before you can engulf chemistry - in fiery death. - type: Tweak - id: 6417 - time: '2024-04-22T08:44:14.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27187 -- author: Potato1234_x - changes: - - message: Added Psicodine, Mannitol, Lipolicide and Happiness. - type: Add - id: 6418 - time: '2024-04-22T08:45:39.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27134 -- author: Terraspark4941 - changes: - - message: Updated the engineering section of the guidebook! - type: Tweak - id: 6419 - time: '2024-04-22T08:58:54.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26851 -- author: eclips_e - changes: - - message: Slimepeople can now morph into a "geras"--a smaller slime form that can - pass under grilles, at the cost of dropping all of their inventory. They can - also be picked up with two hands and placed into duffelbags. - type: Add - - message: Slimepeople now have an internal 2x2 storage that they (and anyone around - them) can access. It is not dropped when morphing into a geras! - type: Add - - message: Slimepeople now have slightly increased regeneration and a slightly meatier - punch, but slower attacks. - type: Add - id: 6420 - time: '2024-04-22T10:03:03.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/23425 -- author: FungiFellow - changes: - - message: Syndi-Cats are now 6TC, Insulated, Available to Syndies, Can Move in - Space, Open Doors, and Hit Harder - type: Tweak - id: 6421 - time: '2024-04-22T12:18:28.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27222 - author: Tayrtahn changes: - message: Ghosts can no longer trigger artifacts by examining them. @@ -3821,3 +3706,102 @@ id: 6908 time: '2024-07-12T09:24:08.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29900 +- author: coffeeware, slarticodefast + changes: + - message: Fixed items thrown very fast not being in the air. In particular this + fixes the pneumatic cannon. + type: Fix + - message: Buffed base hand throwing speed by 10% to be more similar to before the + recent throwing changes. + type: Tweak + id: 6909 + time: '2024-07-12T10:32:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29935 +- author: themias + changes: + - message: Timers can now be deconstructed + type: Fix + id: 6910 + time: '2024-07-12T11:38:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29917 +- author: jonathanargo + changes: + - message: Hard hat icon sprites are now centered correctly. + type: Fix + id: 6911 + time: '2024-07-12T15:00:40.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29953 +- author: lzk228 + changes: + - message: Secure windoors now use reinforced glass damage modifier. + type: Fix + id: 6912 + time: '2024-07-13T04:03:16.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29941 +- author: Tayrtahn + changes: + - message: Antag objective total difficulty is now properly capped. + type: Fix + id: 6913 + time: '2024-07-13T04:14:30.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29830 +- author: EmoGarbage404 + changes: + - message: Added wristwatches for telling time. + type: Add + id: 6914 + time: '2024-07-13T06:09:19.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29550 +- author: TheShuEd + changes: + - message: Added diamonds ore! + type: Add + - message: Diamonds can now be sold at a bargain price. + type: Add + id: 6915 + time: '2024-07-13T12:15:57.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25750 +- author: coffeeware + changes: + - message: Lizards will no longer lose their snouts when equipping head bandanas + type: Fix + id: 6916 + time: '2024-07-14T02:59:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29979 +- author: SlamBamActionman + changes: + - message: RGBee and Rainbow Carp plushies now cycle color when held/worn. + type: Fix + id: 6917 + time: '2024-07-14T10:26:34.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/30023 +- author: Winkarst-cpu + changes: + - message: Now grappling gun is clumsy proof. + type: Tweak + id: 6918 + time: '2024-07-14T10:26:56.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29904 +- author: HahayesSiH + changes: + - message: It is now possible to pet cyborgs. + type: Add + - message: Clicking on cyborgs and opening the strip menu no longer unlocks them. + type: Tweak + id: 6919 + time: '2024-07-14T14:09:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/30037 +- author: deltanedas + changes: + - message: Fixed ninja shoes not working as magboots. + type: Fix + id: 6920 + time: '2024-07-14T15:11:40.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28586 +- author: lzk228 + changes: + - message: Scarves are eatable again. + type: Fix + id: 6921 + time: '2024-07-14T15:12:25.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29959 diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index f3d458e046..47b90c9a16 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aeshus, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, ArkiveDev, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, bhenrich, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blueDev2, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CaasGit, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, DexlerXD, dffdff2423, diraven, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, freeman2651, Froffy025, Fromoriss, FungiFellow, Futuristic-OK, GalacticChimp, gbasood, Geekyhobo, Genkail, geraeumig, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, Interrobang01, IProduceWidgets, ItsMeThom, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JIPDawg, JoeHammad1844, joelsgp, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, Keer-Sar, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, laok233, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, MjrLandWhale, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, RamZ, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, WarMechanic, waylon531, weaversam8, whateverusername0, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zonespace27, Zumorica, Zymem +0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aeshus, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, ArkiveDev, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, bhenrich, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blueDev2, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CaasGit, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, CatTheSystem, Centronias, chairbender, Charlese2, chavonadelal, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, DexlerXD, dffdff2423, diraven, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FoLoKe, fooberticus, Fortune117, freeman2651, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, geraeumig, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, Interrobang01, IProduceWidgets, ItsMeThom, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JIPDawg, JoeHammad1844, joelsgp, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, Keer-Sar, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, laok233, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, MjrLandWhale, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, RamZ, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, WarMechanic, waylon531, weaversam8, whateverusername0, Willhelm53, Winkarst-cpu, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zonespace27, Zumorica, Zymem diff --git a/Resources/Locale/en-US/administration/commands/aghost.ftl b/Resources/Locale/en-US/administration/commands/aghost.ftl index 4de0639981..30cd893dc8 100644 --- a/Resources/Locale/en-US/administration/commands/aghost.ftl +++ b/Resources/Locale/en-US/administration/commands/aghost.ftl @@ -1,3 +1,3 @@ -aghost-description = Makes you an admin ghost. +cmd-aghost-desc = Makes you or others an admin ghost. aghost-no-mind-self = You can't ghost here! aghost-no-mind-other = They can't ghost here! diff --git a/Resources/Locale/en-US/devices/clock.ftl b/Resources/Locale/en-US/devices/clock.ftl new file mode 100644 index 0000000000..6d0aef1eb7 --- /dev/null +++ b/Resources/Locale/en-US/devices/clock.ftl @@ -0,0 +1 @@ +clock-examine = The time reads: [color=white]{$time}[/color] diff --git a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl index 55be1fb3b9..10773d6de8 100644 --- a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl +++ b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl @@ -60,12 +60,26 @@ petting-success-honkbot = You pet {THE($target)} on {POSS-ADJ($target)} slippery petting-success-mimebot = You pet {THE($target)} on {POSS-ADJ($target)} cold metal head. petting-success-cleanbot = You pet {THE($target)} on {POSS-ADJ($target)} damp metal head. petting-success-medibot = You pet {THE($target)} on {POSS-ADJ($target)} sterile metal head. +petting-success-generic-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} metal head. +petting-success-salvage-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} dirty metal head. +petting-success-engineer-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} reflective metal head. +petting-success-janitor-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} damp metal head. +petting-success-medical-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} sterile metal head. +petting-success-service-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} dapper looking metal head. +petting-success-syndicate-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} menacing metal head. petting-success-recycler = You pet {THE($target)} on {POSS-ADJ($target)} mildly threatening steel exterior. petting-failure-honkbot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BASIC($target, "honk", "honks")} in refusal! petting-failure-cleanbot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy mopping! petting-failure-mimebot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy miming! petting-failure-medibot = You reach out to pet {THE($target)}, but {POSS-ADJ($target)} syringe nearly stabs your hand! +petting-failure-generic-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy stating laws! +petting-failure-salvage-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy drilling! +petting-failure-engineer-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy repairing! +petting-failure-janitor-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy cleaning! +petting-failure-medical-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy saving lives! +petting-failure-service-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy serving others! +petting-failure-syndicate-cyborg = You reach out to pet {THE($target)}, but {POSS-ADJ($target)} treacherous affiliation makes you reconsider. ## Rattling fences diff --git a/Resources/Locale/en-US/materials/materials.ftl b/Resources/Locale/en-US/materials/materials.ftl index a354423d2b..0fc716bda5 100644 --- a/Resources/Locale/en-US/materials/materials.ftl +++ b/Resources/Locale/en-US/materials/materials.ftl @@ -25,6 +25,7 @@ materials-meat = meat materials-web = silk materials-bones = bone materials-coal = coal +materials-diamond = diamond materials-gunpowder = gunpowder # Ores @@ -36,3 +37,4 @@ materials-raw-plasma = raw plasma materials-raw-uranium = raw uranium materials-raw-bananium = raw bananium materials-raw-salt = raw salt +materials-raw-diamond = raw diamond diff --git a/Resources/Locale/en-US/salvage/salvage-magnet.ftl b/Resources/Locale/en-US/salvage/salvage-magnet.ftl index 7ce2a486de..c60bafcc13 100644 --- a/Resources/Locale/en-US/salvage/salvage-magnet.ftl +++ b/Resources/Locale/en-US/salvage/salvage-magnet.ftl @@ -13,6 +13,7 @@ salvage-magnet-resources = {$resource -> [OreQuartz] Quartz [OreSalt] Salt [OreGold] Gold + [OreDiamond] Diamond [OreSilver] Silver [OrePlasma] Plasma [OreUranium] Uranium diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index fac36ba710..b49fa383bc 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -170,6 +170,8 @@ prob: 0.20 - id: BarberScissors prob: 0.05 + - id: Wristwatch + prob: 0.05 - id: BookRandomStory prob: 0.1 # Syndicate loot diff --git a/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml b/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml index 51a56f1f1d..da56194f71 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml @@ -20,6 +20,8 @@ - state: icon_mask map: [ "unfoldedLayer" ] visible: false + - type: HideLayerClothing # needed since head bandana inherits from mask bandana + slots: [] - type: Tag tags: - Bandana diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index d220d55f1f..8eeb82cbf4 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -391,7 +391,6 @@ - type: HideLayerClothing slots: - Hair - - Snout - HeadTop - HeadSide diff --git a/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml b/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml index f5ad2fb6c8..8021030095 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml @@ -11,6 +11,9 @@ - HEAD unfoldedSlots: - MASK + foldedHideLayers: [] + unfoldedHideLayers: + - Snout - type: Mask - type: IngestionBlocker - type: IdentityBlocker diff --git a/Resources/Prototypes/Entities/Clothing/Neck/base_clothingneck.yml b/Resources/Prototypes/Entities/Clothing/Neck/base_clothingneck.yml index d2fffb8153..d7f04f49bc 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/base_clothingneck.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/base_clothingneck.yml @@ -38,3 +38,4 @@ - type: Tag tags: - Scarf + - ClothMade diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml b/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml index 4ae752345a..80d5eab249 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml @@ -121,6 +121,7 @@ - type: Clothing sprite: Clothing/Shoes/Specific/spaceninja.rsi - type: NoSlip + - type: Magboots # always have gravity because le suction cups - type: ClothingSpeedModifier # ninja are masters of sneaking around relatively quickly, won't break cloak walkModifier: 1.1 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml index 7d7d9697d2..00798b36e0 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml @@ -67,6 +67,7 @@ - ClothingHeadHatCowboyBountyHunter - ClothingNeckAutismPin - ClothingNeckGoldAutismPin + - WristwatchGold rareChance: 0.01 prototypes: - Lighter @@ -128,6 +129,7 @@ - ClothingShoesTourist - ClothingUniformJumpsuitLoungewear - ClothingHeadHatCowboyRed + - Wristwatch chance: 0.6 offset: 0.0 diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 6656a7aadb..2618207b7a 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -150,6 +150,7 @@ - type: Lock locked: true breakOnEmag: false + unlockOnClick: false - type: ActivatableUIRequiresLock - type: LockedWiresPanel - type: Damageable diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml index 075ac534c6..3a17869dc8 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml @@ -29,6 +29,11 @@ node: cyborg - type: Speech speechVerb: Robotic + - type: InteractionPopup + interactSuccessString: petting-success-generic-cyborg + interactFailureString: petting-failure-generic-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg - type: entity id: BorgChassisMining @@ -85,6 +90,11 @@ access: [["Cargo"], ["Salvage"], ["Command"], ["Research"]] - type: Inventory templateId: borgTall + - type: InteractionPopup + interactSuccessString: petting-success-salvage-cyborg + interactFailureString: petting-failure-salvage-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg - type: entity id: BorgChassisEngineer @@ -133,6 +143,11 @@ access: [["Engineering"], ["Command"], ["Research"]] - type: Inventory templateId: borgShort + - type: InteractionPopup + interactSuccessString: petting-success-engineer-cyborg + interactFailureString: petting-failure-engineer-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg - type: entity id: BorgChassisJanitor @@ -189,6 +204,11 @@ access: [["Service"], ["Command"], ["Research"]] - type: Inventory templateId: borgShort + - type: InteractionPopup + interactSuccessString: petting-success-janitor-cyborg + interactFailureString: petting-failure-janitor-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg - type: entity id: BorgChassisMedical @@ -248,6 +268,11 @@ - type: FootstepModifier footstepSoundCollection: collection: FootstepHoverBorg + - type: InteractionPopup + interactSuccessString: petting-success-medical-cyborg + interactFailureString: petting-failure-medical-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg - type: entity id: BorgChassisService @@ -296,6 +321,11 @@ access: [["Service"], ["Command"], ["Research"]] - type: Inventory templateId: borgTall + - type: InteractionPopup + interactSuccessString: petting-success-service-cyborg + interactFailureString: petting-failure-service-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg - type: entity id: BorgChassisSyndicateAssault @@ -325,6 +355,11 @@ noMindState: synd_sec - type: Construction node: syndicateassault + - type: InteractionPopup + interactSuccessString: petting-success-syndicate-cyborg + interactFailureString: petting-failure-syndicate-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg - type: entity id: BorgChassisSyndicateMedical @@ -357,6 +392,11 @@ - type: ShowHealthBars damageContainers: - Biological + - type: InteractionPopup + interactSuccessString: petting-success-syndicate-cyborg + interactFailureString: petting-failure-syndicate-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg - type: entity id: BorgChassisSyndicateSaboteur @@ -390,3 +430,8 @@ damageContainers: - Inorganic - Silicon + - type: InteractionPopup + interactSuccessString: petting-success-syndicate-cyborg + interactFailureString: petting-failure-syndicate-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index 172ed66cc3..d34b1b536b 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -337,7 +337,8 @@ - type: Sprite sprite: Mobs/Silicon/Bots/supplybot.rsi layers: - - state: supplybot + - map: ["enum.DamageStateVisualLayers.Base", "movement"] + state: supplybot - type: SpriteMovement movementLayers: movement: diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index a0eb01e03b..0c814e08f7 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -69,6 +69,7 @@ - type: RadarConsole followEntity: true - type: CargoOrderConsole + - type: BankClient - type: CrewMonitoringConsole - type: GeneralStationRecordConsole canDeleteEntries: true diff --git a/Resources/Prototypes/Entities/Mobs/Player/vox.yml b/Resources/Prototypes/Entities/Mobs/Player/vox.yml index e7ad39d731..28906584cb 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/vox.yml @@ -1,5 +1,5 @@ - type: entity save: false - name: Urist McVox + name: Uristititi McVox parent: BaseMobVox - id: MobVox \ No newline at end of file + id: MobVox diff --git a/Resources/Prototypes/Entities/Objects/Devices/wristwatch.yml b/Resources/Prototypes/Entities/Objects/Devices/wristwatch.yml new file mode 100644 index 0000000000..6359f659b5 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/wristwatch.yml @@ -0,0 +1,59 @@ +- type: entity + id: Wristwatch + parent: BaseItem + name: wristwatch + description: A cheap watch for telling time. How much did you waste working on this shift? + components: + - type: Sprite + sprite: Objects/Devices/wristwatch.rsi + layers: + - state: wristwatch + - map: [ "enum.ClockVisualLayers.MinuteHand"] + - map: [ "enum.ClockVisualLayers.HourHand"] + - type: Clock + - type: Item + sprite: Objects/Devices/wristwatch.rsi + size: Small + - type: Clothing + sprite: Objects/Devices/wristwatch.rsi + slots: + - gloves + - type: Appearance + - type: Damageable + damageContainer: Inorganic + - type: StaticPrice + price: 50 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageGroupTrigger + damageGroup: Brute + damage: 25 + behaviors: + - !type:DoActsBehavior + acts: [ "Breakage" ] + +- type: entity + id: WristwatchGold + parent: Wristwatch + name: gold watch + description: A fancy watch worth more than your kidney. It was owned by the notorious Syndicate mobster Vunibaldo "200 Pound Horse Meat Grinder" Frediani. + components: + - type: Sprite + sprite: Objects/Devices/goldwatch.rsi + layers: + - state: goldwatch + - map: [ "enum.ClockVisualLayers.MinuteHand"] + - map: [ "enum.ClockVisualLayers.HourHand"] + - type: Item + sprite: Objects/Devices/goldwatch.rsi + - type: Clothing + sprite: Objects/Devices/goldwatch.rsi + - type: StaticPrice + price: 500 #if you ever increase the price of kidneys, increase this too. diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index 310f92e60c..33a322cb25 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -184,6 +184,19 @@ energy: 2 - type: RgbLightController layers: [ 0 ] + - type: Item + inhandVisuals: + left: + - state: bee-inhand-left + shader: unshaded + right: + - state: bee-inhand-right + shader: unshaded + - type: Clothing + clothingVisuals: + head: + - state: bee-equipped-HELMET + shader: unshaded - type: entity parent: BasePlushie @@ -543,6 +556,15 @@ energy: 2 - type: RgbLightController layers: [ 0 ] + - type: Item + heldPrefix: rainbowcarpplush + inhandVisuals: + left: + - state: rainbowcarpplush-inhand-left + shader: unshaded + right: + - state: rainbowcarpplush-inhand-right + shader: unshaded - type: entity parent: PlushieCarp diff --git a/Resources/Prototypes/Entities/Objects/Materials/materials.yml b/Resources/Prototypes/Entities/Objects/Materials/materials.yml index 70d340b2b3..a74a542bb2 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/materials.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/materials.yml @@ -353,8 +353,21 @@ components: - type: Stack stackType: Diamond + baseLayer: base + layerStates: + - diamond + - diamond_2 + - diamond_3 - type: Sprite state: diamond + layers: + - state: diamond + map: ["base"] + - type: StaticPrice + price: 0 + - type: StackPrice + price: 500 + - type: Appearance - type: Item heldPrefix: diamond - type: Extractable diff --git a/Resources/Prototypes/Entities/Objects/Materials/ore.yml b/Resources/Prototypes/Entities/Objects/Materials/ore.yml index bf7dbfad5a..136d20cc81 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/ore.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/ore.yml @@ -66,6 +66,37 @@ - type: Stack count: 1 +- type: entity + parent: OreBase + id: DiamondOre + name: diamond ore + suffix: Full + components: + - type: Stack + stackType: DiamondOre + - type: Sprite + state: diamond + - type: Material + - type: PhysicalComposition + materialComposition: + RawDiamond: 500 + - type: Extractable + grindableSolutionName: diamondore + - type: SolutionContainerManager + solutions: + diamondore: + reagents: + - ReagentId: Carbon + Quantity: 20 + +- type: entity + parent: DiamondOre + id: DiamondOre1 + suffix: Single + components: + - type: Stack + count: 1 + - type: entity parent: OreBase id: SteelOre diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index 5aaa58dbbe..89b421c97d 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -7,8 +7,8 @@ - type: Sprite sprite: Objects/Misc/fire_extinguisher.rsi layers: - - state: fire_extinguisher_closed - map: [ "enabled" ] + - state: fire_extinguisher_closed + map: [ "enum.ToggleVisuals.Layer" ] - type: Item sprite: Objects/Misc/fire_extinguisher.rsi size: Normal @@ -24,6 +24,8 @@ - type: DrainableSolution solution: spray - type: SolutionTransfer + maxTransferAmount: 100 + transferAmount: 100 - type: UseDelay - type: Spray transferAmount: 10 @@ -34,8 +36,20 @@ vaporAmount: 3 vaporSpread: 90 sprayVelocity: 2.0 - - type: FireExtinguisher - hasSafety: true + - type: ItemToggle + soundActivate: + path: /Audio/Machines/button.ogg + params: + variation: 0.125 + volume: -4 + soundDeactivate: + path: /Audio/Machines/button.ogg + params: + variation: 0.125 + volume: -4 + - type: ToggleVerb + text: fire-extinguisher-component-verb-text + - type: SpraySafety - type: MeleeWeapon wideAnimationRotation: 180 damage: @@ -46,14 +60,14 @@ - type: Tool qualities: - Rolling - speedModifier: 0.5 # its very big, akward to use + speedModifier: 0.5 # its very big, awkward to use - type: Appearance - type: GenericVisualizer visuals: - enum.FireExtinguisherVisuals.Safety: - enabled: - True: { state: fire_extinguisher_closed } - False: { state: fire_extinguisher_open } + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: + True: { state: fire_extinguisher_open } + False: { state: fire_extinguisher_closed } - type: PhysicalComposition materialComposition: Steel: 100 @@ -107,4 +121,4 @@ - type: PhysicalComposition materialComposition: Steel: 50 - Glass: 40 \ No newline at end of file + Glass: 40 diff --git a/Resources/Prototypes/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/Entities/Objects/Misc/paper.yml index 1c8d875488..5dbafa6416 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/paper.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/paper.yml @@ -548,6 +548,7 @@ tags: - Write - type: CargoOrderConsole + - type: BankClient - type: ActivatableUI verbText: qm-clipboard-computer-verb-text key: enum.CargoConsoleUiKey.Orders diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index 19a0cf30e8..0ecad70a64 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -251,6 +251,7 @@ - type: Gun soundGunshot: /Audio/Weapons/Guns/Gunshots/harpoon.ogg fireRate: 0.5 + clumsyProof: true - type: BasicEntityAmmoProvider proto: GrapplingHook capacity: 1 diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml index 1ec6c04ffe..d15294cc46 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml @@ -172,6 +172,8 @@ - state: panel_open map: [ "enum.WiresVisualLayers.MaintenancePanel" ] visible: false + - type: Damageable + damageModifierSet: RGlass - type: Destructible thresholds: - trigger: @@ -257,7 +259,7 @@ - type: entity id: BaseSecurePlasmaWindoor - parent: BaseWindoor + parent: BaseSecureWindoor abstract: true components: - type: Sprite @@ -367,7 +369,7 @@ - type: entity id: BaseSecureUraniumWindoor - parent: BaseWindoor + parent: BaseSecureWindoor abstract: true components: - type: Sprite diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 3f9a9bc222..4baf9bb5dc 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -735,6 +735,7 @@ - map: ["computerLayerKeys"] state: tech_key - type: CargoOrderConsole + - type: BankClient - type: ActiveRadio channels: - Supply diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 67cf6f767d..66f4f684e9 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -1175,6 +1175,7 @@ - IngotGold30 - IngotSilver30 - MaterialBananium10 + - MaterialDiamond - type: entity parent: OreProcessor diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml index dd7eb5bea8..271a9a65cd 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml @@ -44,6 +44,14 @@ - type: Construction graph: Timer node: signal + containers: + - board + - type: ContainerContainer + containers: + board: !type:Container + - type: ContainerFill + containers: + board: [ SignalTimerElectronics ] - type: entity id: ScreenTimer @@ -67,6 +75,11 @@ - type: Construction graph: Timer node: screen + containers: + - board + - type: ContainerFill + containers: + board: [ ScreenTimerElectronics ] - type: entity id: BrigTimer @@ -79,6 +92,11 @@ - type: Construction graph: Timer node: brig + containers: + - board + - type: ContainerFill + containers: + board: [ BrigTimerElectronics ] # Construction Frame diff --git a/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml b/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml index e14bf26e0d..4673405e03 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml @@ -93,6 +93,28 @@ state: rock_asteroid_west - state: rock_gold +- type: entity + id: AsteroidRockDiamond + parent: AsteroidRock + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_asteroid + - map: [ "enum.EdgeLayer.South" ] + state: rock_asteroid_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_asteroid_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_asteroid_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_asteroid_west + - state: rock_diamond + - type: entity id: AsteroidRockPlasma parent: AsteroidRock @@ -589,6 +611,28 @@ state: ironrock_west - state: rock_artifact_fragment +- type: entity + id: IronRockDiamond + parent: IronRock + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: ironrock + - map: [ "enum.EdgeLayer.South" ] + state: ironrock_south + - map: [ "enum.EdgeLayer.East" ] + state: ironrock_east + - map: [ "enum.EdgeLayer.North" ] + state: ironrock_north + - map: [ "enum.EdgeLayer.West" ] + state: ironrock_west + - state: rock_diamond + # Rocks and ore veins - type: entity id: WallRock @@ -693,6 +737,28 @@ state: rock_west - state: rock_gold +- type: entity + id: WallRockDiamond + parent: WallRock + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock + - map: [ "enum.EdgeLayer.South" ] + state: rock_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_west + - state: rock_diamond + - type: entity id: WallRockPlasma parent: WallRock @@ -993,6 +1059,28 @@ state: rock_wall_west - state: rock_gold +- type: entity + id: WallRockBasaltDiamond + parent: WallRockBasalt + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_wall + - map: [ "enum.EdgeLayer.South" ] + state: rock_wall_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_wall_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_wall_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_wall_west + - state: rock_diamond + - type: entity id: WallRockBasaltPlasma parent: WallRockBasalt @@ -1236,6 +1324,28 @@ state: rock_snow_west - state: rock_gold +- type: entity + id: WallRockSnowDiamond + parent: WallRockSnow + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_snow + - map: [ "enum.EdgeLayer.South" ] + state: rock_snow_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_snow_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_snow_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_snow_west + - state: rock_diamond + - type: entity id: WallRockSnowPlasma parent: WallRockSnow @@ -1479,6 +1589,28 @@ state: rock_sand_west - state: rock_gold +- type: entity + id: WallRockSandDiamond + parent: WallRockSand + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_sand + - map: [ "enum.EdgeLayer.South" ] + state: rock_sand_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_sand_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_sand_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_sand_west + - state: rock_diamond + - type: entity id: WallRockSandPlasma parent: WallRockSand @@ -1589,7 +1721,6 @@ state: rock_sand_west - state: rock_uranium - - type: entity id: WallRockSandBananium parent: WallRockSand @@ -1722,6 +1853,28 @@ state: rock_chromite_west - state: rock_gold +- type: entity + id: WallRockChromiteDiamond + parent: WallRockChromite + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_chromite + - map: [ "enum.EdgeLayer.South" ] + state: rock_chromite_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_chromite_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_chromite_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_chromite_west + - state: rock_diamond + - type: entity id: WallRockChromitePlasma parent: WallRockChromite @@ -1965,6 +2118,28 @@ state: rock_andesite_west - state: rock_gold +- type: entity + id: WallRockAndesiteDiamond + parent: WallRockAndesite + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_andesite + - map: [ "enum.EdgeLayer.South" ] + state: rock_andesite_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_andesite_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_andesite_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_andesite_west + - state: rock_diamond + - type: entity id: WallRockAndesitePlasma parent: WallRockAndesite diff --git a/Resources/Prototypes/Entities/Virtual/virtual_item.yml b/Resources/Prototypes/Entities/Virtual/virtual_item.yml index ed74243550..088de6a6da 100644 --- a/Resources/Prototypes/Entities/Virtual/virtual_item.yml +++ b/Resources/Prototypes/Entities/Virtual/virtual_item.yml @@ -5,4 +5,5 @@ noSpawn: true components: - type: Item + size: Ginormous # no storage insertion visuals - type: VirtualItem diff --git a/Resources/Prototypes/Entities/World/Debris/asteroids.yml b/Resources/Prototypes/Entities/World/Debris/asteroids.yml index 061288d010..2817b083be 100644 --- a/Resources/Prototypes/Entities/World/Debris/asteroids.yml +++ b/Resources/Prototypes/Entities/World/Debris/asteroids.yml @@ -32,6 +32,9 @@ - id: WallRockGold prob: 0.05 orGroup: rock + - id: WallRockDiamond + prob: 0.005 + orGroup: rock - id: WallRockSilver prob: 0.05 orGroup: rock diff --git a/Resources/Prototypes/Procedural/Magnet/asteroid.yml b/Resources/Prototypes/Procedural/Magnet/asteroid.yml index c20b80af55..c380dfbc7c 100644 --- a/Resources/Prototypes/Procedural/Magnet/asteroid.yml +++ b/Resources/Prototypes/Procedural/Magnet/asteroid.yml @@ -6,6 +6,7 @@ OreCoal: 1.0 OreSalt: 1.0 OreGold: 0.25 + OreDiamond: 0.05 OreSilver: 0.25 OrePlasma: 0.15 OreUranium: 0.15 diff --git a/Resources/Prototypes/Procedural/biome_ore_templates.yml b/Resources/Prototypes/Procedural/biome_ore_templates.yml index 4a60427e30..a6e5fac2d8 100644 --- a/Resources/Prototypes/Procedural/biome_ore_templates.yml +++ b/Resources/Prototypes/Procedural/biome_ore_templates.yml @@ -130,6 +130,20 @@ maxGroupSize: 10 radius: 4 +- type: biomeMarkerLayer + id: OreDiamond + entityMask: + AsteroidRock: AsteroidRockDiamond + WallRock: WallRockDiamond + WallRockBasalt: WallRockBasaltDiamond + WallRockChromite: WallRockChromiteDiamond + WallRockSand: WallRockSandDiamond + WallRockSnow: WallRockSnowDiamond + maxCount: 6 + minGroupSize: 1 + maxGroupSize: 2 + radius: 4 + # Artifact Fragment - type: biomeMarkerLayer id: OreArtifactFragment diff --git a/Resources/Prototypes/Procedural/salvage_loot.yml b/Resources/Prototypes/Procedural/salvage_loot.yml index e8783760dd..da99da7c75 100644 --- a/Resources/Prototypes/Procedural/salvage_loot.yml +++ b/Resources/Prototypes/Procedural/salvage_loot.yml @@ -178,6 +178,13 @@ - !type:BiomeMarkerLoot proto: OreBananium +- type: salvageLoot + id: OreDiamond + guaranteed: true + loots: + - !type:BiomeMarkerLoot + proto: OreDiamond + - type: salvageLoot id: OreArtifactFragment guaranteed: true diff --git a/Resources/Prototypes/Procedural/vgroid.yml b/Resources/Prototypes/Procedural/vgroid.yml index 49e956e73f..6e9fc6f395 100644 --- a/Resources/Prototypes/Procedural/vgroid.yml +++ b/Resources/Prototypes/Procedural/vgroid.yml @@ -82,6 +82,12 @@ count: 50 minGroupSize: 2 maxGroupSize: 4 + - !type:OreDunGen + replacement: IronRock + entity: IronRockDiamond + count: 15 + minGroupSize: 1 + maxGroupSize: 2 # Configs - type: dungeonConfig diff --git a/Resources/Prototypes/Reagents/Materials/ores.yml b/Resources/Prototypes/Reagents/Materials/ores.yml index 18f1d9ebb3..1555ab9e23 100644 --- a/Resources/Prototypes/Reagents/Materials/ores.yml +++ b/Resources/Prototypes/Reagents/Materials/ores.yml @@ -24,6 +24,15 @@ color: "#FFD700" price: 0.2 +- type: material + id: RawDiamond + stackEntity: DiamondOre1 + name: materials-raw-diamond + unit: materials-unit-piece + icon: { sprite: Objects/Materials/ore.rsi, state: diamond } + color: "#C9D8F2" + price: 0.5 + - type: material id: RawSilver stackEntity: SilverOre1 diff --git a/Resources/Prototypes/Recipes/Lathes/sheet.yml b/Resources/Prototypes/Recipes/Lathes/sheet.yml index 053715a181..3efaac95a7 100644 --- a/Resources/Prototypes/Recipes/Lathes/sheet.yml +++ b/Resources/Prototypes/Recipes/Lathes/sheet.yml @@ -127,6 +127,13 @@ materials: RawBananium: 3000 +- type: latheRecipe + id: MaterialDiamond + result: MaterialDiamond1 + completetime: 3 + materials: + RawDiamond: 1000 + - type: latheRecipe id: SheetUranium1 result: SheetUranium1 diff --git a/Resources/Prototypes/Stacks/Materials/ore.yml b/Resources/Prototypes/Stacks/Materials/ore.yml index 51254b5a7a..3aaa04601a 100644 --- a/Resources/Prototypes/Stacks/Materials/ore.yml +++ b/Resources/Prototypes/Stacks/Materials/ore.yml @@ -4,6 +4,13 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: gold } spawn: GoldOre1 maxCount: 30 + +- type: stack + id: DiamondOre + name: rough diamond + icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: diamond } + spawn: DiamondOre1 + maxCount: 30 - type: stack id: SteelOre diff --git a/Resources/Prototypes/ore.yml b/Resources/Prototypes/ore.yml index 84d1c66736..146f04b49c 100644 --- a/Resources/Prototypes/ore.yml +++ b/Resources/Prototypes/ore.yml @@ -58,6 +58,12 @@ minOreYield: 1 maxOreYield: 3 +- type: ore + id: OreDiamond + oreEntity: DiamondOre1 + minOreYield: 1 + maxOreYield: 2 + - type: ore id: OreQuartzCrab oreEntity: MobSpawnCrabQuartz diff --git a/Resources/Textures/Clothing/Head/Hardhats/armored.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/armored.rsi/icon.png index 7adc2b1385..fc2328e7d1 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/armored.rsi/icon.png and b/Resources/Textures/Clothing/Head/Hardhats/armored.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/armored.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/armored.rsi/light-icon.png index 3a850ef0d8..a0bef06515 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/armored.rsi/light-icon.png and b/Resources/Textures/Clothing/Head/Hardhats/armored.rsi/light-icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/icon.png index fee14e7a8c..f3d81549e3 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/icon.png and b/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/light-icon.png index 3a850ef0d8..f385d0187d 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/light-icon.png and b/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/light-icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/icon.png index 3d3aa75958..2728bf501b 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/icon.png and b/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/light-icon.png index 3a850ef0d8..f385d0187d 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/light-icon.png and b/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/light-icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/icon.png index ba88c23f72..b4bbfa0e09 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/icon.png and b/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/light-icon.png index 316cf7a9bc..f385d0187d 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/light-icon.png and b/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/light-icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/red.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/red.rsi/icon.png index 011962037d..5a8fb92ef9 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/red.rsi/icon.png and b/Resources/Textures/Clothing/Head/Hardhats/red.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/red.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/red.rsi/light-icon.png index 3a850ef0d8..f385d0187d 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/red.rsi/light-icon.png and b/Resources/Textures/Clothing/Head/Hardhats/red.rsi/light-icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/white.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/white.rsi/icon.png index 2a5093d619..06af0dcd0a 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/white.rsi/icon.png and b/Resources/Textures/Clothing/Head/Hardhats/white.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/white.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/white.rsi/light-icon.png index 316cf7a9bc..f385d0187d 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/white.rsi/light-icon.png and b/Resources/Textures/Clothing/Head/Hardhats/white.rsi/light-icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/icon.png index 1a3f49cc1b..0978e7886c 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/icon.png and b/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/light-icon.png index 3a850ef0d8..f385d0187d 100644 Binary files a/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/light-icon.png and b/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/light-icon.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/equipped-HAND.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/equipped-HAND.png new file mode 100644 index 0000000000..59522f6c12 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/equipped-HAND.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/goldwatch.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/goldwatch.png new file mode 100644 index 0000000000..60e16eb845 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/goldwatch.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_0.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_0.png new file mode 100644 index 0000000000..f1064c1a27 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_0.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_1.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_1.png new file mode 100644 index 0000000000..c553b1c913 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_1.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_10.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_10.png new file mode 100644 index 0000000000..0bbe9d8ba2 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_10.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_11.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_11.png new file mode 100644 index 0000000000..e364438e4e Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_11.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_2.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_2.png new file mode 100644 index 0000000000..87c642239f Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_2.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_3.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_3.png new file mode 100644 index 0000000000..107b210ff4 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_3.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_4.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_4.png new file mode 100644 index 0000000000..c03f799fcc Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_4.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_5.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_5.png new file mode 100644 index 0000000000..2835fadd24 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_5.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_6.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_6.png new file mode 100644 index 0000000000..55df41a9ce Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_6.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_7.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_7.png new file mode 100644 index 0000000000..890650474d Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_7.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_8.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_8.png new file mode 100644 index 0000000000..a9a27f92df Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_8.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_9.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_9.png new file mode 100644 index 0000000000..262901db8f Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_9.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-left.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-left.png new file mode 100644 index 0000000000..ec40991d4e Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-right.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-right.png new file mode 100644 index 0000000000..aac69417e3 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/meta.json b/Resources/Textures/Objects/Devices/goldwatch.rsi/meta.json new file mode 100644 index 0000000000..6466438b53 --- /dev/null +++ b/Resources/Textures/Objects/Devices/goldwatch.rsi/meta.json @@ -0,0 +1,98 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from vgstation13 at https://github.com/vgstation-coders/vgstation13/blob/223bf37a7386249ecf1fe425ca8cef25821ca9d7/icons/obj/watches/goldwatch.dmi, https://github.com/vgstation-coders/vgstation13/blob/223bf37a7386249ecf1fe425ca8cef25821ca9d7/icons/mob/in-hand/right/clothing_accessories.dmi, https://github.com/vgstation-coders/vgstation13/blob/223bf37a7386249ecf1fe425ca8cef25821ca9d7/icons/mob/in-hand/left/clothing_accessories.dmi, https://github.com/vgstation-coders/vgstation13/blob/30f9caeb59b0dd9da1dbcd4c69307ae182033a74/icons/mob/clothing_accessories.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "goldwatch" + }, + { + "name": "hours_1" + }, + { + "name": "hours_2" + }, + { + "name": "hours_3" + }, + { + "name": "hours_4" + }, + { + "name": "hours_5" + }, + { + "name": "hours_6" + }, + { + "name": "hours_7" + }, + { + "name": "hours_8" + }, + { + "name": "hours_9" + }, + { + "name": "hours_10" + }, + { + "name": "hours_11" + }, + { + "name": "hours_0" + }, + { + "name": "minutes_1" + }, + { + "name": "minutes_2" + }, + { + "name": "minutes_3" + }, + { + "name": "minutes_4" + }, + { + "name": "minutes_5" + }, + { + "name": "minutes_6" + }, + { + "name": "minutes_7" + }, + { + "name": "minutes_8" + }, + { + "name": "minutes_9" + }, + { + "name": "minutes_10" + }, + { + "name": "minutes_11" + }, + { + "name": "minutes_0" + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "equipped-HAND", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_0.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_0.png new file mode 100644 index 0000000000..be687566cb Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_0.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_1.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_1.png new file mode 100644 index 0000000000..0ef2c831f4 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_1.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_10.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_10.png new file mode 100644 index 0000000000..3019a20a9b Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_10.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_11.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_11.png new file mode 100644 index 0000000000..fcbf039b6f Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_11.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_2.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_2.png new file mode 100644 index 0000000000..1304f56e75 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_2.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_3.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_3.png new file mode 100644 index 0000000000..8f58881dbd Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_3.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_4.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_4.png new file mode 100644 index 0000000000..a0c007b6ea Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_4.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_5.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_5.png new file mode 100644 index 0000000000..1d71f45c47 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_5.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_6.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_6.png new file mode 100644 index 0000000000..5e1070e3a8 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_6.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_7.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_7.png new file mode 100644 index 0000000000..4af1070d21 Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_7.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_8.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_8.png new file mode 100644 index 0000000000..39b252688a Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_8.png differ diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_9.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_9.png new file mode 100644 index 0000000000..b9614ed80f Binary files /dev/null and b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_9.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/equipped-HAND.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/equipped-HAND.png new file mode 100644 index 0000000000..b12afe6d4e Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/equipped-HAND.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_0.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_0.png new file mode 100644 index 0000000000..3af9ca4634 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_0.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_1.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_1.png new file mode 100644 index 0000000000..4791cee3e4 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_1.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_10.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_10.png new file mode 100644 index 0000000000..aeac862656 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_10.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_11.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_11.png new file mode 100644 index 0000000000..5f2daf512a Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_11.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_2.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_2.png new file mode 100644 index 0000000000..386166cabd Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_2.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_3.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_3.png new file mode 100644 index 0000000000..2f5300928f Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_3.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_4.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_4.png new file mode 100644 index 0000000000..c994c05685 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_4.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_5.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_5.png new file mode 100644 index 0000000000..c344b0307b Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_5.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_6.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_6.png new file mode 100644 index 0000000000..892194a6f0 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_6.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_7.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_7.png new file mode 100644 index 0000000000..fa7af8e5cc Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_7.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_8.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_8.png new file mode 100644 index 0000000000..83caf8c8e1 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_8.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_9.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_9.png new file mode 100644 index 0000000000..6334bb72fd Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_9.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-left.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-left.png new file mode 100644 index 0000000000..76ca3c4911 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-right.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-right.png new file mode 100644 index 0000000000..f9ab9c67d4 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/meta.json b/Resources/Textures/Objects/Devices/wristwatch.rsi/meta.json new file mode 100644 index 0000000000..fbf18c1a8d --- /dev/null +++ b/Resources/Textures/Objects/Devices/wristwatch.rsi/meta.json @@ -0,0 +1,98 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from vgstation13 at https://github.com/vgstation-coders/vgstation13/blob/223bf37a7386249ecf1fe425ca8cef25821ca9d7/icons/obj/watches/wristwatch.dmi, https://github.com/vgstation-coders/vgstation13/blob/30f9caeb59b0dd9da1dbcd4c69307ae182033a74/icons/obj/clothing/accessory_overlays.dmi, https://github.com/vgstation-coders/vgstation13/blob/223bf37a7386249ecf1fe425ca8cef25821ca9d7/icons/mob/in-hand/right/clothing_accessories.dmi, https://github.com/vgstation-coders/vgstation13/blob/223bf37a7386249ecf1fe425ca8cef25821ca9d7/icons/mob/in-hand/right/clothing_accessories.dmi, https://github.com/vgstation-coders/vgstation13/blob/223bf37a7386249ecf1fe425ca8cef25821ca9d7/icons/mob/in-hand/left/clothing_accessories.dmi, https://github.com/vgstation-coders/vgstation13/blob/30f9caeb59b0dd9da1dbcd4c69307ae182033a74/icons/mob/clothing_accessories.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "wristwatch" + }, + { + "name": "hours_1" + }, + { + "name": "hours_2" + }, + { + "name": "hours_3" + }, + { + "name": "hours_4" + }, + { + "name": "hours_5" + }, + { + "name": "hours_6" + }, + { + "name": "hours_7" + }, + { + "name": "hours_8" + }, + { + "name": "hours_9" + }, + { + "name": "hours_10" + }, + { + "name": "hours_11" + }, + { + "name": "hours_0" + }, + { + "name": "minutes_1" + }, + { + "name": "minutes_2" + }, + { + "name": "minutes_3" + }, + { + "name": "minutes_4" + }, + { + "name": "minutes_5" + }, + { + "name": "minutes_6" + }, + { + "name": "minutes_7" + }, + { + "name": "minutes_8" + }, + { + "name": "minutes_9" + }, + { + "name": "minutes_10" + }, + { + "name": "minutes_11" + }, + { + "name": "minutes_0" + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "equipped-HAND", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_0.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_0.png new file mode 100644 index 0000000000..5285c6b709 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_0.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_1.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_1.png new file mode 100644 index 0000000000..336bb51039 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_1.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_10.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_10.png new file mode 100644 index 0000000000..9dad0d83cb Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_10.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_11.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_11.png new file mode 100644 index 0000000000..0c789112c3 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_11.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_2.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_2.png new file mode 100644 index 0000000000..a4c3bbe717 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_2.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_3.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_3.png new file mode 100644 index 0000000000..6bfdc405c7 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_3.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_4.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_4.png new file mode 100644 index 0000000000..89c08f3396 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_4.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_5.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_5.png new file mode 100644 index 0000000000..4cbaa00d14 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_5.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_6.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_6.png new file mode 100644 index 0000000000..e834c52d8c Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_6.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_7.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_7.png new file mode 100644 index 0000000000..8c8927f03a Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_7.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_8.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_8.png new file mode 100644 index 0000000000..e0bba2ecf9 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_8.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_9.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_9.png new file mode 100644 index 0000000000..db0f5a1c71 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_9.png differ diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/wristwatch.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/wristwatch.png new file mode 100644 index 0000000000..aea09a7f89 Binary files /dev/null and b/Resources/Textures/Objects/Devices/wristwatch.rsi/wristwatch.png differ diff --git a/Resources/Textures/Objects/Fun/toys.rsi/meta.json b/Resources/Textures/Objects/Fun/toys.rsi/meta.json index fc92a47936..fa118695c2 100644 --- a/Resources/Textures/Objects/Fun/toys.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/toys.rsi/meta.json @@ -53,6 +53,14 @@ { "name": "rainbowcarpplush" }, + { + "name": "rainbowcarpplush-inhand-left", + "directions": 4 + }, + { + "name": "rainbowcarpplush-inhand-right", + "directions": 4 + }, { "name": "narplush" }, diff --git a/Resources/Textures/Objects/Fun/toys.rsi/rainbowcarpplush-inhand-left.png b/Resources/Textures/Objects/Fun/toys.rsi/rainbowcarpplush-inhand-left.png new file mode 100644 index 0000000000..8f28f41fd8 Binary files /dev/null and b/Resources/Textures/Objects/Fun/toys.rsi/rainbowcarpplush-inhand-left.png differ diff --git a/Resources/Textures/Objects/Fun/toys.rsi/rainbowcarpplush-inhand-right.png b/Resources/Textures/Objects/Fun/toys.rsi/rainbowcarpplush-inhand-right.png new file mode 100644 index 0000000000..f12cf346ba Binary files /dev/null and b/Resources/Textures/Objects/Fun/toys.rsi/rainbowcarpplush-inhand-right.png differ diff --git a/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-left.png b/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-left.png index c9b55e9daa..9eae45d039 100644 Binary files a/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-left.png and b/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-left.png differ diff --git a/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-right.png b/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-right.png index 295c2c4eca..3353eb6f7c 100644 Binary files a/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-right.png and b/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-right.png differ diff --git a/Resources/Textures/Objects/Materials/materials.rsi/diamond.png b/Resources/Textures/Objects/Materials/materials.rsi/diamond.png index 5eb8aabf87..8b39437d0a 100644 Binary files a/Resources/Textures/Objects/Materials/materials.rsi/diamond.png and b/Resources/Textures/Objects/Materials/materials.rsi/diamond.png differ diff --git a/Resources/Textures/Objects/Materials/materials.rsi/diamond_2.png b/Resources/Textures/Objects/Materials/materials.rsi/diamond_2.png new file mode 100644 index 0000000000..410d83f1c2 Binary files /dev/null and b/Resources/Textures/Objects/Materials/materials.rsi/diamond_2.png differ diff --git a/Resources/Textures/Objects/Materials/materials.rsi/diamond_3.png b/Resources/Textures/Objects/Materials/materials.rsi/diamond_3.png new file mode 100644 index 0000000000..152da2befe Binary files /dev/null and b/Resources/Textures/Objects/Materials/materials.rsi/diamond_3.png differ diff --git a/Resources/Textures/Objects/Materials/materials.rsi/meta.json b/Resources/Textures/Objects/Materials/materials.rsi/meta.json index 78f497c0cd..3ae12dc14c 100644 --- a/Resources/Textures/Objects/Materials/materials.rsi/meta.json +++ b/Resources/Textures/Objects/Materials/materials.rsi/meta.json @@ -78,6 +78,12 @@ { "name": "diamond" }, + { + "name": "diamond_2" + }, + { + "name": "diamond_3" + }, { "name": "diamond-inhand-left", "directions": 4 diff --git a/Resources/Textures/Objects/Materials/ore.rsi/diamond.png b/Resources/Textures/Objects/Materials/ore.rsi/diamond.png new file mode 100644 index 0000000000..b2a3c788d3 Binary files /dev/null and b/Resources/Textures/Objects/Materials/ore.rsi/diamond.png differ diff --git a/Resources/Textures/Objects/Materials/ore.rsi/meta.json b/Resources/Textures/Objects/Materials/ore.rsi/meta.json index 98a10750bf..8f1b4533f2 100644 --- a/Resources/Textures/Objects/Materials/ore.rsi/meta.json +++ b/Resources/Textures/Objects/Materials/ore.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "silver, plasma taken from https://github.com/vgstation-coders/vgstation13 at commit f2ef221849675915a78fd92fe622c32ab740e085, spacequartz taken from https://github.com/goonstation/goonstation at commit b51daf824df46a3a1426475f982c09479818e522 and reshaded by Alekshhh, bananium; uranium; iron; gold; coal by Alekshhh", + "copyright": "silver, plasma taken from https://github.com/vgstation-coders/vgstation13 at commit f2ef221849675915a78fd92fe622c32ab740e085, spacequartz taken from https://github.com/goonstation/goonstation at commit b51daf824df46a3a1426475f982c09479818e522 and reshaded by Alekshhh, bananium; uranium; iron; gold; coal by Alekshhh, diamond at commit https://github.com/tgstation/tgstation/pull/78524, edited by TheShuEd", "size": { "x": 32, "y": 32 @@ -33,6 +33,9 @@ }, { "name": "salt" + }, + { + "name": "diamond" } ] } diff --git a/Resources/Textures/Structures/Walls/rock.rsi/meta.json b/Resources/Textures/Structures/Walls/rock.rsi/meta.json index aa46326a4b..a29513356c 100644 --- a/Resources/Textures/Structures/Walls/rock.rsi/meta.json +++ b/Resources/Textures/Structures/Walls/rock.rsi/meta.json @@ -104,7 +104,23 @@ "name": "rock_copper" }, { - "name": "rock_diamond" + "name": "rock_diamond", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] }, { "name": "rock_gold" diff --git a/Resources/Textures/Structures/Walls/rock.rsi/rock_diamond.png b/Resources/Textures/Structures/Walls/rock.rsi/rock_diamond.png index eb05ebd3d1..cc54b642f4 100644 Binary files a/Resources/Textures/Structures/Walls/rock.rsi/rock_diamond.png and b/Resources/Textures/Structures/Walls/rock.rsi/rock_diamond.png differ diff --git a/RobustToolbox b/RobustToolbox index a9aea7027f..fc1cca4f48 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit a9aea7027f1840c83bcaf1c973caf099745f9eed +Subproject commit fc1cca4f48f2f2d3fbf41aa42b80b4e43b1095a4 diff --git a/SpaceStation14.sln b/SpaceStation14.sln index e0cb455a6d..bcd013b598 100644 --- a/SpaceStation14.sln +++ b/SpaceStation14.sln @@ -62,7 +62,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{806ED41A-411B-4B3B-BEB6-DEC6DCA4C205}" ProjectSection(SolutionItems) = preProject Tools\generate_hashes.ps1 = Tools\generate_hashes.ps1 - Tools\gen_build_info.py = Tools\gen_build_info.py EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Robust.Shared.Scripting", "RobustToolbox\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj", "{41B450C0-A361-4CD7-8121-7072B8995CFC}" diff --git a/Tools/actions_changelog_rss.py b/Tools/actions_changelog_rss.py index 5e42a030bd..01ca7852cc 100755 --- a/Tools/actions_changelog_rss.py +++ b/Tools/actions_changelog_rss.py @@ -29,7 +29,7 @@ CHANGELOG_RSS_KEY = os.environ.get("CHANGELOG_RSS_KEY") # Change these to suit your server settings # https://docs.fabfile.org/en/stable/getting-started.html#run-commands-via-connections-and-run -SSH_HOST = "centcomm.spacestation14.io" +SSH_HOST = "moon.spacestation14.com" SSH_USER = "changelog-rss" SSH_PORT = 22 RSS_FILE = "changelog.xml" diff --git a/Tools/gen_build_info.py b/Tools/gen_build_info.py deleted file mode 100755 index 0207f568dd..0000000000 --- a/Tools/gen_build_info.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python3 - -# Generates build info and injects it into the server zip files. - -import codecs -import hashlib -import io -import json -import os -import subprocess -from zipfile import ZipFile, ZIP_DEFLATED - -FILE = "SS14.Client.zip" - -SERVER_FILES = [ - "SS14.Server_linux-x64.zip", - "SS14.Server_linux-arm64.zip", - "SS14.Server_win-x64.zip", - "SS14.Server_osx-x64.zip" -] - -VERSION = os.environ['GITHUB_SHA'] -FORK_ID = "wizards" -BUILD_URL = f"https://cdn.centcomm.spacestation14.com/builds/wizards/builds/{{FORK_VERSION}}/{FILE}" -MANIFEST_URL = f"https://cdn.centcomm.spacestation14.com/cdn/version/{{FORK_VERSION}}/manifest" -MANIFEST_DOWNLOAD_URL = f"https://cdn.centcomm.spacestation14.com/cdn/version/{{FORK_VERSION}}/download" - -def main() -> None: - client_file = os.path.join("release", FILE) - manifest = generate_build_json(client_file) - - for server in SERVER_FILES: - inject_manifest(os.path.join("release", server), manifest) - - -def inject_manifest(zip_path: str, manifest: str) -> None: - with ZipFile(zip_path, "a", compression=ZIP_DEFLATED) as z: - z.writestr("build.json", manifest) - - -def generate_build_json(file: str) -> str: - # Env variables set by Jenkins. - - hash = sha256_file(file) - engine_version = get_engine_version() - manifest_hash = generate_manifest_hash(file) - - return json.dumps({ - "download": BUILD_URL, - "hash": hash, - "version": VERSION, - "fork_id": FORK_ID, - "engine_version": engine_version, - "manifest_url": MANIFEST_URL, - "manifest_download_url": MANIFEST_DOWNLOAD_URL, - "manifest_hash": manifest_hash - }) - -def generate_manifest_hash(file: str) -> str: - zip = ZipFile(file) - infos = zip.infolist() - infos.sort(key=lambda i: i.filename) - - bytesIO = io.BytesIO() - writer = codecs.getwriter("UTF-8")(bytesIO) - writer.write("Robust Content Manifest 1\n") - - for info in infos: - if info.filename[-1] == "/": - continue - - bytes = zip.read(info) - hash = hashlib.blake2b(bytes, digest_size=32).hexdigest().upper() - writer.write(f"{hash} {info.filename}\n") - - manifestHash = hashlib.blake2b(bytesIO.getbuffer(), digest_size=32) - - return manifestHash.hexdigest().upper() - -def get_engine_version() -> str: - proc = subprocess.run(["git", "describe","--tags", "--abbrev=0"], stdout=subprocess.PIPE, cwd="RobustToolbox", check=True, encoding="UTF-8") - tag = proc.stdout.strip() - assert tag.startswith("v") - return tag[1:] # Cut off v prefix. - - -def sha256_file(path: str) -> str: - with open(path, "rb") as f: - h = hashlib.sha256() - for b in iter(lambda: f.read(4096), b""): - h.update(b) - - return h.hexdigest() - -if __name__ == '__main__': - main() diff --git a/Tools/publish_github_artifact.py b/Tools/publish_github_artifact.py new file mode 100755 index 0000000000..b488ccdf2e --- /dev/null +++ b/Tools/publish_github_artifact.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +import requests +import os +import subprocess + +GITHUB_TOKEN = os.environ["GITHUB_TOKEN"] +PUBLISH_TOKEN = os.environ["PUBLISH_TOKEN"] +ARTIFACT_ID = os.environ["ARTIFACT_ID"] +GITHUB_REPOSITORY = os.environ["GITHUB_REPOSITORY"] +VERSION = os.environ['GITHUB_SHA'] + +# +# CONFIGURATION PARAMETERS +# Forks should change these to publish to their own infrastructure. +# +ROBUST_CDN_URL = "https://wizards.cdn.spacestation14.com/" +FORK_ID = "wizards" + +def main(): + print("Fetching artifact URL from API...") + artifact_url = get_artifact_url() + print(f"Artifact URL is {artifact_url}, publishing to Robust.Cdn") + + data = { + "version": VERSION, + "engineVersion": get_engine_version(), + "archive": artifact_url + } + headers = { + "Authorization": f"Bearer {PUBLISH_TOKEN}", + "Content-Type": "application/json" + } + resp = requests.post(f"{ROBUST_CDN_URL}fork/{FORK_ID}/publish", json=data, headers=headers) + resp.raise_for_status() + print("Publish succeeded!") + +def get_artifact_url() -> str: + headers = { + "Authorization": f"Bearer {GITHUB_TOKEN}", + "X-GitHub-Api-Version": "2022-11-28" + } + resp = requests.get(f"https://api.github.com/repos/{GITHUB_REPOSITORY}/actions/artifacts/{ARTIFACT_ID}/zip", allow_redirects=False, headers=headers) + resp.raise_for_status() + + return resp.headers["Location"] + +def get_engine_version() -> str: + proc = subprocess.run(["git", "describe","--tags", "--abbrev=0"], stdout=subprocess.PIPE, cwd="RobustToolbox", check=True, encoding="UTF-8") + tag = proc.stdout.strip() + assert tag.startswith("v") + return tag[1:] # Cut off v prefix. + + +if __name__ == '__main__': + main()