From edf766103117132baeb12a1fbbf8150164f330c4 Mon Sep 17 00:00:00 2001 From: null <56081759+NullWanderer@users.noreply.github.com> Date: Sat, 18 May 2024 13:26:23 +0200 Subject: [PATCH 001/215] Revert "Temporary CI/CD fix (#1199)" This reverts commit 7e3ba621d325db4d838c4d0fb675b5173f3111df. --- .github/workflows/build-map-renderer.yml | 2 +- .github/workflows/build-test-debug.yml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/test-packaging.yml | 2 +- .github/workflows/yaml-linter.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-map-renderer.yml b/.github/workflows/build-map-renderer.yml index e16d951cfae..01575f64b9b 100644 --- a/.github/workflows/build-map-renderer.yml +++ b/.github/workflows/build-map-renderer.yml @@ -36,7 +36,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.100 + dotnet-version: 8.0.x - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/build-test-debug.yml b/.github/workflows/build-test-debug.yml index 70dc7d3b11c..519f5af6f49 100644 --- a/.github/workflows/build-test-debug.yml +++ b/.github/workflows/build-test-debug.yml @@ -36,7 +36,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.100 + dotnet-version: 8.0.x - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 177e6a0fe6d..74a975be5c6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,7 +22,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.100 + dotnet-version: 8.0.x - name: Get Engine Tag run: | diff --git a/.github/workflows/test-packaging.yml b/.github/workflows/test-packaging.yml index 745f8c092c4..ccece69adb6 100644 --- a/.github/workflows/test-packaging.yml +++ b/.github/workflows/test-packaging.yml @@ -51,7 +51,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.100 + dotnet-version: 8.0.x - name: Install dependencies run: dotnet restore diff --git a/.github/workflows/yaml-linter.yml b/.github/workflows/yaml-linter.yml index d24991ec963..796795b234d 100644 --- a/.github/workflows/yaml-linter.yml +++ b/.github/workflows/yaml-linter.yml @@ -26,7 +26,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v3.2.0 with: - dotnet-version: 8.0.100 + dotnet-version: 8.0.x - name: Install dependencies run: dotnet restore - name: Build From 0edbdd1f808af7400588a0d6ab992c020e54decf Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Tue, 7 May 2024 06:02:22 +0300 Subject: [PATCH 002/215] Night on Europa (#27731) night --- Content.Server/Parallax/BiomeSystem.cs | 4 ++-- Content.Server/Station/Components/StationBiomeComponent.cs | 3 +++ Content.Server/Station/Systems/StationBiomeSystem.cs | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Content.Server/Parallax/BiomeSystem.cs b/Content.Server/Parallax/BiomeSystem.cs index 83cf9b9cb2d..0543518dcc9 100644 --- a/Content.Server/Parallax/BiomeSystem.cs +++ b/Content.Server/Parallax/BiomeSystem.cs @@ -973,7 +973,7 @@ private void UnloadChunk(BiomeComponent component, EntityUid gridUid, MapGridCom /// /// Creates a simple planet setup for a map. /// - public void EnsurePlanet(EntityUid mapUid, BiomeTemplatePrototype biomeTemplate, int? seed = null, MetaDataComponent? metadata = null) + public void EnsurePlanet(EntityUid mapUid, BiomeTemplatePrototype biomeTemplate, int? seed = null, MetaDataComponent? metadata = null, Color? mapLight = null) { if (!Resolve(mapUid, ref metadata)) return; @@ -998,7 +998,7 @@ public void EnsurePlanet(EntityUid mapUid, BiomeTemplatePrototype biomeTemplate, // Lava: #A34931 var light = EnsureComp(mapUid); - light.AmbientLightColor = Color.FromHex("#D8B059"); + light.AmbientLightColor = mapLight ?? Color.FromHex("#D8B059"); Dirty(mapUid, light, metadata); var moles = new float[Atmospherics.AdjustedNumberOfGases]; diff --git a/Content.Server/Station/Components/StationBiomeComponent.cs b/Content.Server/Station/Components/StationBiomeComponent.cs index 38800579a93..0eb64aaff80 100644 --- a/Content.Server/Station/Components/StationBiomeComponent.cs +++ b/Content.Server/Station/Components/StationBiomeComponent.cs @@ -16,4 +16,7 @@ public sealed partial class StationBiomeComponent : Component // If null, its random [DataField] public int? Seed = null; + + [DataField] + public Color MapLightColor = Color.Black; } diff --git a/Content.Server/Station/Systems/StationBiomeSystem.cs b/Content.Server/Station/Systems/StationBiomeSystem.cs index 821745fc4d7..8c80806ccf5 100644 --- a/Content.Server/Station/Systems/StationBiomeSystem.cs +++ b/Content.Server/Station/Systems/StationBiomeSystem.cs @@ -30,6 +30,6 @@ private void OnStationPostInit(Entity map, ref StationPos var mapId = Transform(station.Value).MapID; var mapUid = _mapManager.GetMapEntityId(mapId); - _biome.EnsurePlanet(mapUid, _proto.Index(map.Comp.Biome), map.Comp.Seed); + _biome.EnsurePlanet(mapUid, _proto.Index(map.Comp.Biome), map.Comp.Seed, mapLight: map.Comp.MapLightColor); } } From 6d4841701228d99a53066c392a51105992a83176 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 7 May 2024 05:23:50 +0200 Subject: [PATCH 003/215] Reduce ratking chance severely (#27760) --- Resources/Prototypes/GameRules/events.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 7b4f421f853..0f46a54b368 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -200,10 +200,11 @@ - id: MobMouse2 prob: 0.02 # DeltaV - Rat King spawns under MidRoundAntag - Comment out Rat King from spawning with MouseMigration gamerule - # specialEntries: - # - id: SpawnPointGhostRatKing - # prob: 0.005 - # End of modified code +# - id: MobMouseCancer +# prob: 0.001 +# specialEntries: +# - id: SpawnPointGhostRatKing +# prob: 0.001 - type: entity id: CockroachMigration From 5ba6cbf65d670ed2827546b82ea5e71b11dac883 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 7 May 2024 03:25:14 +0000 Subject: [PATCH 004/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 47d361f0aea..f6691435281 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Zadeon - changes: - - message: Added salt as a new type of ore that can be mined from asteroids. Salt - can be ground up for its base components. - type: Add - id: 6043 - time: '2024-02-26T23:34:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25324 - author: juliangiebel changes: - message: The mass media consoles UI got overhauled @@ -3858,3 +3850,10 @@ id: 6542 time: '2024-05-07T01:48:16.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26629 +- author: mirrorcult + changes: + - message: Less ratkings should spawn during the mouse migration event now + type: Tweak + id: 6543 + time: '2024-05-07T03:23:50.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27760 From 8689c7f90d0441e89846db98aca56f785fb25229 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Tue, 7 May 2024 06:21:03 +0200 Subject: [PATCH 005/215] Fix preference loading bugs (#27742) First bug: if an error occured during pref loading code, it would fail. If the person then readied up, it would likely cause the round to fail to start. Why could they ready up? The code only checks that the prefs finished loading, not that they finished loading *successfully*. Whoops. Anyways, now people get kicked if their prefs fail to load. And I improved the error handling. Second bug: if a user disconnected while their prefs were loading, it would cause an exception. This exception would go unobserved on lobby servers or raise through gameticker on non-lobby servers. This happened even on a live server once and then triggered the first bug, but idk how. Fixed this by properly plumbing through cancellation into the preferences loading code. The stuff is now cancelled properly. Third bug: if somebody has a loadout item with a playtime requirement active, load-time sanitization of player prefs could run into a race condition because the sanitization can happen *before* play time was loaded. Fixed by moving pref sanitizations to a later stage in the load process. --- Content.Server/Database/ServerDbBase.cs | 34 +++++----- Content.Server/Database/ServerDbManager.cs | 24 ++++--- Content.Server/Database/ServerDbPostgres.cs | 14 +++-- Content.Server/Database/ServerDbSqlite.cs | 20 +++--- Content.Server/Database/UserDbDataManager.cs | 62 +++++++++++++++++-- .../GameTicking/GameTicker.Player.cs | 24 ++++++- .../PlayTimeTrackingManager.cs | 2 +- .../Managers/IServerPreferencesManager.cs | 1 + .../Managers/ServerPreferencesManager.cs | 28 ++++++--- 9 files changed, 155 insertions(+), 54 deletions(-) diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index 9d40d9400f3..12d220512b7 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -33,9 +33,11 @@ public ServerDbBase(ISawmill opsLog) } #region Preferences - public async Task GetPlayerPreferencesAsync(NetUserId userId) + public async Task GetPlayerPreferencesAsync( + NetUserId userId, + CancellationToken cancel = default) { - await using var db = await GetDb(); + await using var db = await GetDb(cancel); var prefs = await db.DbContext .Preference @@ -47,7 +49,7 @@ public ServerDbBase(ISawmill opsLog) .ThenInclude(l => l.Groups) .ThenInclude(group => group.Loadouts) .AsSingleQuery() - .SingleOrDefaultAsync(p => p.UserId == userId.UserId); + .SingleOrDefaultAsync(p => p.UserId == userId.UserId, cancel); if (prefs is null) return null; @@ -515,13 +517,13 @@ public async Task EditServerRoleBan(int id, string reason, NoteSeverity severity #endregion #region Playtime - public async Task> GetPlayTimes(Guid player) + public async Task> GetPlayTimes(Guid player, CancellationToken cancel) { - await using var db = await GetDb(); + await using var db = await GetDb(cancel); return await db.DbContext.PlayTime .Where(p => p.PlayerId == player) - .ToListAsync(); + .ToListAsync(cancel); } public async Task UpdatePlayTimes(IReadOnlyCollection updates) @@ -673,7 +675,7 @@ public async Task AddServerBanHitsAsync(int connection, IEnumerable GetAdminDataForAsync(NetUserId userId, CancellationToken cancel) { - await using var db = await GetDb(); + await using var db = await GetDb(cancel); return await db.DbContext.Admin .Include(p => p.Flags) @@ -688,7 +690,7 @@ public async Task AddServerBanHitsAsync(int connection, IEnumerable GetAdminRankDataForAsync(int id, CancellationToken cancel = default) { - await using var db = await GetDb(); + await using var db = await GetDb(cancel); return await db.DbContext.AdminRank .Include(r => r.Flags) @@ -697,7 +699,7 @@ public async Task AddServerBanHitsAsync(int connection, IEnumerable a.UserId == userId.UserId, cancel); db.DbContext.Admin.Remove(admin); @@ -707,7 +709,7 @@ public async Task RemoveAdminAsync(NetUserId userId, CancellationToken cancel) public async Task AddAdminAsync(Admin admin, CancellationToken cancel) { - await using var db = await GetDb(); + await using var db = await GetDb(cancel); db.DbContext.Admin.Add(admin); @@ -716,7 +718,7 @@ public async Task AddAdminAsync(Admin admin, CancellationToken cancel) public async Task UpdateAdminAsync(Admin admin, CancellationToken cancel) { - await using var db = await GetDb(); + await using var db = await GetDb(cancel); var existing = await db.DbContext.Admin.Include(a => a.Flags).SingleAsync(a => a.UserId == admin.UserId, cancel); existing.Flags = admin.Flags; @@ -728,7 +730,7 @@ public async Task UpdateAdminAsync(Admin admin, CancellationToken cancel) public async Task RemoveAdminRankAsync(int rankId, CancellationToken cancel) { - await using var db = await GetDb(); + await using var db = await GetDb(cancel); var admin = await db.DbContext.AdminRank.SingleAsync(a => a.Id == rankId, cancel); db.DbContext.AdminRank.Remove(admin); @@ -738,7 +740,7 @@ public async Task RemoveAdminRankAsync(int rankId, CancellationToken cancel) public async Task AddAdminRankAsync(AdminRank rank, CancellationToken cancel) { - await using var db = await GetDb(); + await using var db = await GetDb(cancel); db.DbContext.AdminRank.Add(rank); @@ -811,7 +813,7 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id} public async Task UpdateAdminRankAsync(AdminRank rank, CancellationToken cancel) { - await using var db = await GetDb(); + await using var db = await GetDb(cancel); var existing = await db.DbContext.AdminRank .Include(r => r.Flags) @@ -1594,7 +1596,9 @@ public async Task HasPendingModelChanges() return db.DbContext.Database.HasPendingModelChanges(); } - protected abstract Task GetDb([CallerMemberName] string? name = null); + protected abstract Task GetDb( + CancellationToken cancel = default, + [CallerMemberName] string? name = null); protected void LogDbOp(string? name) { diff --git a/Content.Server/Database/ServerDbManager.cs b/Content.Server/Database/ServerDbManager.cs index 554dd307437..01d15267274 100644 --- a/Content.Server/Database/ServerDbManager.cs +++ b/Content.Server/Database/ServerDbManager.cs @@ -29,7 +29,11 @@ public interface IServerDbManager void Shutdown(); #region Preferences - Task InitPrefsAsync(NetUserId userId, ICharacterProfile defaultProfile); + Task InitPrefsAsync( + NetUserId userId, + ICharacterProfile defaultProfile, + CancellationToken cancel); + Task SaveSelectedCharacterIndexAsync(NetUserId userId, int index); Task SaveCharacterSlotAsync(NetUserId userId, ICharacterProfile? profile, int slot); @@ -38,7 +42,7 @@ public interface IServerDbManager // Single method for two operations for transaction. Task DeleteSlotAndSetSelectedIndex(NetUserId userId, int deleteSlot, int newSlot); - Task GetPlayerPreferencesAsync(NetUserId userId); + Task GetPlayerPreferencesAsync(NetUserId userId, CancellationToken cancel); #endregion #region User Ids @@ -157,8 +161,9 @@ public Task EditServerRoleBan( /// Look up a player's role timers. /// /// The player to get the role timer information from. + /// /// All role timers belonging to the player. - Task> GetPlayTimes(Guid player); + Task> GetPlayTimes(Guid player, CancellationToken cancel = default); /// /// Update play time information in bulk. @@ -346,7 +351,10 @@ public void Shutdown() _sqliteInMemoryConnection?.Dispose(); } - public Task InitPrefsAsync(NetUserId userId, ICharacterProfile defaultProfile) + public Task InitPrefsAsync( + NetUserId userId, + ICharacterProfile defaultProfile, + CancellationToken cancel) { DbWriteOpsMetric.Inc(); return RunDbCommand(() => _db.InitPrefsAsync(userId, defaultProfile)); @@ -376,10 +384,10 @@ public Task SaveAdminOOCColorAsync(NetUserId userId, Color color) return RunDbCommand(() => _db.SaveAdminOOCColorAsync(userId, color)); } - public Task GetPlayerPreferencesAsync(NetUserId userId) + public Task GetPlayerPreferencesAsync(NetUserId userId, CancellationToken cancel) { DbReadOpsMetric.Inc(); - return RunDbCommand(() => _db.GetPlayerPreferencesAsync(userId)); + return RunDbCommand(() => _db.GetPlayerPreferencesAsync(userId, cancel)); } public Task AssignUserIdAsync(string name, NetUserId userId) @@ -487,10 +495,10 @@ public Task EditServerRoleBan(int id, string reason, NoteSeverity severity, Date #region Playtime - public Task> GetPlayTimes(Guid player) + public Task> GetPlayTimes(Guid player, CancellationToken cancel) { DbReadOpsMetric.Inc(); - return RunDbCommand(() => _db.GetPlayTimes(player)); + return RunDbCommand(() => _db.GetPlayTimes(player, cancel)); } public Task UpdatePlayTimes(IReadOnlyCollection updates) diff --git a/Content.Server/Database/ServerDbPostgres.cs b/Content.Server/Database/ServerDbPostgres.cs index c81e735868a..fd4699fff4e 100644 --- a/Content.Server/Database/ServerDbPostgres.cs +++ b/Content.Server/Database/ServerDbPostgres.cs @@ -527,22 +527,26 @@ protected override DateTime NormalizeDatabaseTime(DateTime time) return time; } - private async Task GetDbImpl([CallerMemberName] string? name = null) + private async Task GetDbImpl( + CancellationToken cancel = default, + [CallerMemberName] string? name = null) { LogDbOp(name); await _dbReadyTask; - await _prefsSemaphore.WaitAsync(); + await _prefsSemaphore.WaitAsync(cancel); if (_msLag > 0) - await Task.Delay(_msLag); + await Task.Delay(_msLag, cancel); return new DbGuardImpl(this, new PostgresServerDbContext(_options)); } - protected override async Task GetDb([CallerMemberName] string? name = null) + protected override async Task GetDb( + CancellationToken cancel = default, + [CallerMemberName] string? name = null) { - return await GetDbImpl(name); + return await GetDbImpl(cancel, name); } private sealed class DbGuardImpl : DbGuard diff --git a/Content.Server/Database/ServerDbSqlite.cs b/Content.Server/Database/ServerDbSqlite.cs index 88ecf820020..ffec90bb43d 100644 --- a/Content.Server/Database/ServerDbSqlite.cs +++ b/Content.Server/Database/ServerDbSqlite.cs @@ -439,7 +439,7 @@ public override async Task AddConnectionLogAsync( public override async Task<((Admin, string? lastUserName)[] admins, AdminRank[])> GetAllAdminAndRanksAsync( CancellationToken cancel) { - await using var db = await GetDbImpl(); + await using var db = await GetDbImpl(cancel); var admins = await db.SqliteDbContext.Admin .Include(a => a.Flags) @@ -514,23 +514,27 @@ protected override DateTime NormalizeDatabaseTime(DateTime time) return DateTime.SpecifyKind(time, DateTimeKind.Utc); } - private async Task GetDbImpl([CallerMemberName] string? name = null) + private async Task GetDbImpl( + CancellationToken cancel = default, + [CallerMemberName] string? name = null) { LogDbOp(name); await _dbReadyTask; if (_msDelay > 0) - await Task.Delay(_msDelay); + await Task.Delay(_msDelay, cancel); - await _prefsSemaphore.WaitAsync(); + await _prefsSemaphore.WaitAsync(cancel); var dbContext = new SqliteServerDbContext(_options()); return new DbGuardImpl(this, dbContext); } - protected override async Task GetDb([CallerMemberName] string? name = null) + protected override async Task GetDb( + CancellationToken cancel = default, + [CallerMemberName] string? name = null) { - return await GetDbImpl(name).ConfigureAwait(false); + return await GetDbImpl(cancel, name).ConfigureAwait(false); } private sealed class DbGuardImpl : DbGuard @@ -569,9 +573,9 @@ public ConcurrencySemaphore(int maxCount, bool synchronous) _semaphore = new SemaphoreSlim(maxCount, maxCount); } - public Task WaitAsync() + public Task WaitAsync(CancellationToken cancel = default) { - var task = _semaphore.WaitAsync(); + var task = _semaphore.WaitAsync(cancel); if (_synchronous) { diff --git a/Content.Server/Database/UserDbDataManager.cs b/Content.Server/Database/UserDbDataManager.cs index f8b1611fd57..3f6659840ad 100644 --- a/Content.Server/Database/UserDbDataManager.cs +++ b/Content.Server/Database/UserDbDataManager.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Content.Server.Players.PlayTimeTracking; using Content.Server.Preferences.Managers; +using Robust.Server.Player; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Utility; @@ -16,17 +17,22 @@ namespace Content.Server.Database; /// Actual loading code is handled by separate managers such as . /// This manager is simply a centralized "is loading done" controller for other code to rely on. /// -public sealed class UserDbDataManager +public sealed class UserDbDataManager : IPostInjectInit { [Dependency] private readonly IServerPreferencesManager _prefs = default!; + [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly PlayTimeTrackingManager _playTimeTracking = default!; private readonly Dictionary _users = new(); + private ISawmill _sawmill = default!; + // TODO: Ideally connected/disconnected would be subscribed to IPlayerManager directly, // but this runs into ordering issues with game ticker. public void ClientConnected(ICommonSession session) { + _sawmill.Verbose($"Initiating load for user {session}"); + DebugTools.Assert(!_users.ContainsKey(session.UserId), "We should not have any cached data on client connect."); var cts = new CancellationTokenSource(); @@ -51,11 +57,52 @@ public void ClientDisconnected(ICommonSession session) private async Task Load(ICommonSession session, CancellationToken cancel) { - await Task.WhenAll( - _prefs.LoadData(session, cancel), - _playTimeTracking.LoadData(session, cancel)); + // The task returned by this function is only ever observed by callers of WaitLoadComplete, + // which doesn't even happen currently if the lobby is enabled. + // As such, this task must NOT throw a non-cancellation error! + try + { + await Task.WhenAll( + _prefs.LoadData(session, cancel), + _playTimeTracking.LoadData(session, cancel)); + + cancel.ThrowIfCancellationRequested(); + _prefs.SanitizeData(session); + + _sawmill.Verbose($"Load complete for user {session}"); + } + catch (OperationCanceledException) + { + _sawmill.Debug($"Load cancelled for user {session}"); + + // We can rethrow the cancellation. + // This will make the task returned by WaitLoadComplete() also return a cancellation. + throw; + } + catch (Exception e) + { + // Must catch all exceptions here, otherwise task may go unobserved. + _sawmill.Error($"Load of user data failed: {e}"); + + // Kick them from server, since something is hosed. Let them try again I guess. + session.Channel.Disconnect("Loading of server user data failed, this is a bug."); + + // We throw a OperationCanceledException so users of WaitLoadComplete() always see cancellation here. + throw new OperationCanceledException("Load of user data cancelled due to unknown error"); + } } + /// + /// Wait for all on-database data for a user to be loaded. + /// + /// + /// The task returned by this function may end up in a cancelled state + /// (throwing ) if the user disconnects while loading or an error occurs. + /// + /// + /// + /// A task that completes when all on-database data for a user has finished loading. + /// public Task WaitLoadComplete(ICommonSession session) { return _users[session.UserId].Task; @@ -63,7 +110,7 @@ public Task WaitLoadComplete(ICommonSession session) public bool IsLoadComplete(ICommonSession session) { - return GetLoadTask(session).IsCompleted; + return GetLoadTask(session).IsCompletedSuccessfully; } public Task GetLoadTask(ICommonSession session) @@ -71,5 +118,10 @@ public Task GetLoadTask(ICommonSession session) return _users[session.UserId].Task; } + void IPostInjectInit.PostInject() + { + _sawmill = _logManager.GetSawmill("userdb"); + } + private sealed record UserData(CancellationTokenSource Cancel, Task Task); } diff --git a/Content.Server/GameTicking/GameTicker.Player.cs b/Content.Server/GameTicking/GameTicker.Player.cs index a3e33117cc6..dcf2dc5366d 100644 --- a/Content.Server/GameTicking/GameTicker.Player.cs +++ b/Content.Server/GameTicking/GameTicker.Player.cs @@ -145,13 +145,33 @@ private async void PlayerStatusChanged(object? sender, SessionStatusEventArgs ar async void SpawnWaitDb() { - await _userDb.WaitLoadComplete(session); + try + { + await _userDb.WaitLoadComplete(session); + } + catch (OperationCanceledException) + { + // Bail, user must've disconnected or something. + Log.Debug($"Database load cancelled while waiting to spawn {session}"); + return; + } + SpawnPlayer(session, EntityUid.Invalid); } async void SpawnObserverWaitDb() { - await _userDb.WaitLoadComplete(session); + try + { + await _userDb.WaitLoadComplete(session); + } + catch (OperationCanceledException) + { + // Bail, user must've disconnected or something. + Log.Debug($"Database load cancelled while waiting to spawn {session}"); + return; + } + JoinAsObserver(session); } diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs index 6a1292f5dca..9fcd80b72a3 100644 --- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs +++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs @@ -317,7 +317,7 @@ public async Task LoadData(ICommonSession session, CancellationToken cancel) var data = new PlayTimeData(); _playTimeData.Add(session, data); - var playTimes = await _db.GetPlayTimes(session.UserId); + var playTimes = await _db.GetPlayTimes(session.UserId, cancel); cancel.ThrowIfCancellationRequested(); foreach (var timer in playTimes) diff --git a/Content.Server/Preferences/Managers/IServerPreferencesManager.cs b/Content.Server/Preferences/Managers/IServerPreferencesManager.cs index 1808592ef5a..92e7adf22ad 100644 --- a/Content.Server/Preferences/Managers/IServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/IServerPreferencesManager.cs @@ -12,6 +12,7 @@ public interface IServerPreferencesManager void Init(); Task LoadData(ICommonSession session, CancellationToken cancel); + void SanitizeData(ICommonSession session); void OnClientDisconnected(ICommonSession session); bool TryGetCachedPreferences(NetUserId userId, [NotNullWhen(true)] out PlayerPreferences? playerPreferences); diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index a1eb8aad82b..55532f1ff5b 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -13,6 +13,7 @@ using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Server.Preferences.Managers @@ -27,6 +28,7 @@ public sealed class ServerPreferencesManager : IServerPreferencesManager [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IServerDbManager _db = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IDependencyCollection _dependencies = default!; [Dependency] private readonly IPrototypeManager _protos = default!; // Cache player prefs on the server so we don't need as much async hell related to them. @@ -101,9 +103,8 @@ private async void HandleUpdateCharacterMessage(MsgUpdateCharacter message) var curPrefs = prefsData.Prefs!; var session = _playerManager.GetSessionById(userId); - var collection = IoCManager.Instance!; - profile.EnsureValid(session, collection); + profile.EnsureValid(session, _dependencies); var profiles = new Dictionary(curPrefs.Characters) { @@ -196,7 +197,7 @@ public async Task LoadData(ICommonSession session, CancellationToken cancel) async Task LoadPrefs() { - var prefs = await GetOrCreatePreferencesAsync(session.UserId); + var prefs = await GetOrCreatePreferencesAsync(session.UserId, cancel); prefsData.Prefs = prefs; prefsData.PrefsLoaded = true; @@ -211,6 +212,16 @@ async Task LoadPrefs() } } + public void SanitizeData(ICommonSession session) + { + // This is a separate step from the actual database load. + // Sanitizing preferences requires play time info due to loadouts. + // And play time info is loaded concurrently from the DB with preferences. + var data = _cachedPlayerPrefs[session.UserId]; + DebugTools.Assert(data.Prefs != null); + data.Prefs = SanitizePreferences(session, data.Prefs, _dependencies); + } + public void OnClientDisconnected(ICommonSession session) { _cachedPlayerPrefs.Remove(session.UserId); @@ -270,18 +281,15 @@ public PlayerPreferences GetPreferences(NetUserId userId) return null; } - private async Task GetOrCreatePreferencesAsync(NetUserId userId) + private async Task GetOrCreatePreferencesAsync(NetUserId userId, CancellationToken cancel) { - var prefs = await _db.GetPlayerPreferencesAsync(userId); + var prefs = await _db.GetPlayerPreferencesAsync(userId, cancel); if (prefs is null) { - return await _db.InitPrefsAsync(userId, HumanoidCharacterProfile.Random()); + return await _db.InitPrefsAsync(userId, HumanoidCharacterProfile.Random(), cancel); } - var session = _playerManager.GetSessionById(userId); - var collection = IoCManager.Instance!; - - return SanitizePreferences(session, prefs, collection); + return prefs; } private PlayerPreferences SanitizePreferences(ICommonSession session, PlayerPreferences prefs, IDependencyCollection collection) From 8cffd8e26ee94faf1a42bf2f3f26d8f7c4574c64 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 7 May 2024 04:22:10 +0000 Subject: [PATCH 006/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f6691435281..b49dad26c8d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: juliangiebel - changes: - - message: The mass media consoles UI got overhauled - type: Tweak - - message: Added PDA notifications in chat for new news articles. They can be turned - off in the news reader program and are only visible to you. - type: Add - id: 6044 - time: '2024-02-27T01:38:00.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/19610 - author: rosieposieeee changes: - message: Added lockable wall buttons and decorative frames for differentiating @@ -3857,3 +3847,11 @@ id: 6543 time: '2024-05-07T03:23:50.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27760 +- author: PJB3005 + changes: + - message: Fixed preferences not loading sometimes if you had a loadout item with + playtime requirement selected in a character profile. + type: Fix + id: 6544 + time: '2024-05-07T04:21:03.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27742 From 41408a009c752a5e9293dee6d617d8eaaf362218 Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Tue, 7 May 2024 09:26:33 +0200 Subject: [PATCH 007/215] Little morgue overhaul (#27750) --- Resources/Prototypes/Entities/Structures/Storage/morgue.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Storage/morgue.yml b/Resources/Prototypes/Entities/Structures/Storage/morgue.yml index 725f4c9b0f2..496ea416119 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/morgue.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/morgue.yml @@ -5,6 +5,8 @@ placement: mode: SnapgridCenter components: + - type: Pullable + - type: Anchorable - type: Sprite sprite: Structures/Storage/morgue.rsi layers: @@ -28,11 +30,11 @@ shape: !type:PhysShapeAabb bounds: "-0.5,-0.5,0.5,0.5" - density: 190 + density: 1000 mask: - MachineMask layer: - - WallLayer + - HalfWallLayer - type: EntityStorage isCollidableWhenOpen: true showContents: false From 502d2fe9158d058f31891229a4d5c64dcaf444a9 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 7 May 2024 07:27:38 +0000 Subject: [PATCH 008/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index b49dad26c8d..af0ce0e4ef9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: rosieposieeee - changes: - - message: Added lockable wall buttons and decorative frames for differentiating - wall buttons. - type: Add - id: 6045 - time: '2024-02-27T07:57:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25631 - author: metalgearsloth changes: - message: Fix chat bubbles. @@ -3855,3 +3847,12 @@ id: 6544 time: '2024-05-07T04:21:03.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27742 +- author: lzk228 + changes: + - message: Morgue now can be anchored and unanchored. + type: Tweak + - message: Changed morgue's collision layer, so items now can go through it. + type: Tweak + id: 6545 + time: '2024-05-07T07:26:33.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27750 From 2d48912bc90835df47257f5afe05dd9da1aaeb6f Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Tue, 7 May 2024 03:50:22 -0700 Subject: [PATCH 009/215] Make arguments and parameters wrap to one variable per line (#27766) --- .editorconfig | 4 + .../GameTicking/GameTicker.Spawning.cs | 106 +++++++++++++----- 2 files changed, 79 insertions(+), 31 deletions(-) diff --git a/.editorconfig b/.editorconfig index 872a068c7c6..f01530bcb18 100644 --- a/.editorconfig +++ b/.editorconfig @@ -336,7 +336,11 @@ dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter # ReSharper properties resharper_braces_for_ifelse = required_for_multiline +resharper_csharp_wrap_arguments_style = chop_if_long +resharper_csharp_wrap_parameters_style = chop_if_long resharper_keep_existing_attribute_arrangement = true +resharper_wrap_chained_binary_patterns = chop_if_long +resharper_wrap_chained_method_calls = chop_if_long [*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}] indent_size = 2 diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 157eff8d3f4..dfe8c5ce905 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -55,7 +55,9 @@ private List GetSpawnableStations() return spawnableStations; } - private void SpawnPlayers(List readyPlayers, Dictionary profiles, bool force) + private void SpawnPlayers(List readyPlayers, + Dictionary profiles, + bool force) { // Allow game rules to spawn players by themselves if needed. (For example, nuke ops or wizard) RaiseLocalEvent(new RulePlayerSpawningEvent(readyPlayers, profiles, force)); @@ -94,7 +96,8 @@ private void SpawnPlayers(List readyPlayers, Dictionary readyPlayers, Dictionary _playerManager.GetSessionById(x)).ToArray(), profiles, force)); + RaiseLocalEvent(new RulePlayerJobsAssignedEvent( + assignedJobs.Keys.Select(x => _playerManager.GetSessionById(x)).ToArray(), + profiles, + force)); } - private void SpawnPlayer(ICommonSession player, EntityUid station, string? jobId = null, bool lateJoin = true, bool silent = false) + private void SpawnPlayer(ICommonSession player, + EntityUid station, + string? jobId = null, + bool lateJoin = true, + bool silent = false) { var character = GetPlayerProfile(player); @@ -132,7 +142,12 @@ private void SpawnPlayer(ICommonSession player, EntityUid station, string? jobId SpawnPlayer(player, character, station, jobId, lateJoin, silent); } - private void SpawnPlayer(ICommonSession player, HumanoidCharacterProfile character, EntityUid station, string? jobId = null, bool lateJoin = true, bool silent = false) + private void SpawnPlayer(ICommonSession player, + HumanoidCharacterProfile character, + EntityUid station, + string? jobId = null, + bool lateJoin = true, + bool silent = false) { // Can't spawn players with a dummy ticker! if (DummyTicker) @@ -176,7 +191,9 @@ private void SpawnPlayer(ICommonSession player, HumanoidCharacterProfile charact restrictedRoles.UnionWith(jobBans); // Pick best job best on prefs. - jobId ??= _stationJobs.PickBestAvailableJobWithPriority(station, character.JobPriorities, true, + jobId ??= _stationJobs.PickBestAvailableJobWithPriority(station, + character.JobPriorities, + true, restrictedRoles); // If no job available, stay in lobby, or if no lobby spawn as observer if (jobId is null) @@ -185,7 +202,9 @@ private void SpawnPlayer(ICommonSession player, HumanoidCharacterProfile charact { JoinAsObserver(player); } - _chatManager.DispatchServerMessage(player, Loc.GetString("game-ticker-player-no-jobs-available-when-joining")); + + _chatManager.DispatchServerMessage(player, + Loc.GetString("game-ticker-player-no-jobs-available-when-joining")); return; } @@ -199,7 +218,7 @@ private void SpawnPlayer(ICommonSession player, HumanoidCharacterProfile charact _mind.SetUserId(newMind, data.UserId); var jobPrototype = _prototypeManager.Index(jobId); - var job = new JobComponent { Prototype = jobId }; + var job = new JobComponent {Prototype = jobId}; _roles.MindAddRole(newMind, job, silent: silent); var jobName = _jobs.MindTryGetJobName(newMind); @@ -222,11 +241,10 @@ private void SpawnPlayer(ICommonSession player, HumanoidCharacterProfile charact if (lateJoin && !silent) { _chatSystem.DispatchStationAnnouncement(station, - Loc.GetString( - "latejoin-arrival-announcement", - ("character", MetaData(mob).EntityName), - ("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(jobName)) - ), Loc.GetString("latejoin-arrival-sender"), + Loc.GetString("latejoin-arrival-announcement", + ("character", MetaData(mob).EntityName), + ("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(jobName))), + Loc.GetString("latejoin-arrival-sender"), playDefaultSound: false); } @@ -238,14 +256,17 @@ private void SpawnPlayer(ICommonSession player, HumanoidCharacterProfile charact _stationJobs.TryAssignJob(station, jobPrototype, player.UserId); if (lateJoin) - _adminLogger.Add(LogType.LateJoin, LogImpact.Medium, $"Player {player.Name} late joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}."); + _adminLogger.Add(LogType.LateJoin, + LogImpact.Medium, + $"Player {player.Name} late joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}."); else - _adminLogger.Add(LogType.RoundStartJoin, LogImpact.Medium, $"Player {player.Name} joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}."); + _adminLogger.Add(LogType.RoundStartJoin, + LogImpact.Medium, + $"Player {player.Name} joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}."); // Make sure they're aware of extended access. if (Comp(station).ExtendedAccess - && (jobPrototype.ExtendedAccess.Count > 0 - || jobPrototype.ExtendedAccessGroups.Count > 0)) + && (jobPrototype.ExtendedAccess.Count > 0 || jobPrototype.ExtendedAccessGroups.Count > 0)) { _chatManager.DispatchServerMessage(player, Loc.GetString("job-greet-crew-shortages")); } @@ -267,14 +288,20 @@ private void SpawnPlayer(ICommonSession player, HumanoidCharacterProfile charact } else { - _chatManager.DispatchServerMessage(player, Loc.GetString("latejoin-arrivals-direction-time", - ("time", $"{arrival:mm\\:ss}"))); + _chatManager.DispatchServerMessage(player, + Loc.GetString("latejoin-arrivals-direction-time", ("time", $"{arrival:mm\\:ss}"))); } } // We raise this event directed to the mob, but also broadcast it so game rules can do something now. PlayersJoinedRoundNormally++; - var aev = new PlayerSpawnCompleteEvent(mob, player, jobId, lateJoin, PlayersJoinedRoundNormally, station, character); + var aev = new PlayerSpawnCompleteEvent(mob, + player, + jobId, + lateJoin, + PlayersJoinedRoundNormally, + station, + character); RaiseLocalEvent(mob, aev, true); } @@ -296,7 +323,10 @@ public void Respawn(ICommonSession player) /// The station they're spawning on /// An optional job for them to spawn as /// Whether or not the player should be greeted upon joining - public void MakeJoinGame(ICommonSession player, EntityUid station, string? jobId = null, bool silent = false) + public void MakeJoinGame(ICommonSession player, + EntityUid station, + string? jobId = null, + bool silent = false) { if (!_playerGameStatuses.ContainsKey(player.UserId)) return; @@ -342,23 +372,29 @@ public void SpawnObserver(ICommonSession player) _metaData.SetEntityName(ghost, name); _ghost.SetCanReturnToBody(ghost, false); _mind.TransferTo(mind.Value, ghost); - _adminLogger.Add(LogType.LateJoin, LogImpact.Low, $"{player.Name} late joined the round as an Observer with {ToPrettyString(ghost):entity}."); + _adminLogger.Add(LogType.LateJoin, + LogImpact.Low, + $"{player.Name} late joined the round as an Observer with {ToPrettyString(ghost):entity}."); } #region Mob Spawning Helpers + private EntityUid SpawnObserverMob() { var coordinates = GetObserverSpawnPoint(); return EntityManager.SpawnEntity(ObserverPrototypeName, coordinates); } + #endregion #region Spawn Points + public EntityCoordinates GetObserverSpawnPoint() { _possiblePositions.Clear(); - foreach (var (point, transform) in EntityManager.EntityQuery(true)) + foreach (var (point, transform) in EntityManager + .EntityQuery(true)) { if (point.SpawnType != SpawnPointType.Observer) continue; @@ -374,8 +410,7 @@ public EntityCoordinates GetObserverSpawnPoint() var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var grid)) { - if (!metaQuery.TryGetComponent(uid, out var meta) || - meta.EntityPaused) + if (!metaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused) { continue; } @@ -396,8 +431,7 @@ public EntityCoordinates GetObserverSpawnPoint() { var gridXform = Transform(gridUid); - return new EntityCoordinates(gridUid, - gridXform.InvWorldMatrix.Transform(toMap.Position)); + return new EntityCoordinates(gridUid, gridXform.InvWorldMatrix.Transform(toMap.Position)); } return spawn; @@ -413,8 +447,7 @@ public EntityCoordinates GetObserverSpawnPoint() { var mapUid = _mapManager.GetMapEntityId(map); - if (!metaQuery.TryGetComponent(mapUid, out var meta) || - meta.EntityPaused) + if (!metaQuery.TryGetComponent(mapUid, out var meta) || meta.EntityPaused) { continue; } @@ -427,6 +460,7 @@ public EntityCoordinates GetObserverSpawnPoint() _sawmill.Warning("Found no observer spawn points!"); return EntityCoordinates.Invalid; } + #endregion } @@ -444,7 +478,11 @@ public sealed class PlayerBeforeSpawnEvent : HandledEntityEventArgs public bool LateJoin { get; } public EntityUid Station { get; } - public PlayerBeforeSpawnEvent(ICommonSession player, HumanoidCharacterProfile profile, string? jobId, bool lateJoin, EntityUid station) + public PlayerBeforeSpawnEvent(ICommonSession player, + HumanoidCharacterProfile profile, + string? jobId, + bool lateJoin, + EntityUid station) { Player = player; Profile = profile; @@ -472,7 +510,13 @@ public sealed class PlayerSpawnCompleteEvent : EntityEventArgs // Ex. If this is the 27th person to join, this will be 27. public int JoinOrder { get; } - public PlayerSpawnCompleteEvent(EntityUid mob, ICommonSession player, string? jobId, bool lateJoin, int joinOrder, EntityUid station, HumanoidCharacterProfile profile) + public PlayerSpawnCompleteEvent(EntityUid mob, + ICommonSession player, + string? jobId, + bool lateJoin, + int joinOrder, + EntityUid station, + HumanoidCharacterProfile profile) { Mob = mob; Player = player; From b59ffd6673332d92a006769472c151316681ef43 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Tue, 7 May 2024 20:52:18 +1000 Subject: [PATCH 010/215] Revert "Fix turned off thrusters consume power" (#27755) Revert "Fix turned off thrusters consume power (#26690)" This reverts commit 70959e7bb081c1a6e1457a8f8ee7732da91bb270. --- Content.Server/Shuttles/Systems/ThrusterSystem.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Content.Server/Shuttles/Systems/ThrusterSystem.cs b/Content.Server/Shuttles/Systems/ThrusterSystem.cs index be55cd9a62a..74c42ccbc53 100644 --- a/Content.Server/Shuttles/Systems/ThrusterSystem.cs +++ b/Content.Server/Shuttles/Systems/ThrusterSystem.cs @@ -264,11 +264,6 @@ public void EnableThruster(EntityUid uid, ThrusterComponent component, Transform return; } - if (TryComp(uid, out var apcPower)) - { - apcPower.NeedsPower = true; - } - component.IsOn = true; if (!EntityManager.TryGetComponent(xform.GridUid, out ShuttleComponent? shuttleComponent)) @@ -371,11 +366,6 @@ public void DisableThruster(EntityUid uid, ThrusterComponent component, EntityUi if (!EntityManager.TryGetComponent(gridId, out ShuttleComponent? shuttleComponent)) return; - if (TryComp(uid, out var apcPower)) - { - apcPower.NeedsPower = false; - } - // Logger.DebugS("thruster", $"Disabled thruster {uid}"); switch (component.Type) From 93093f8a25e9a24cbdfb849a8ac065d7a637facc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20M=C4=99drek?= Date: Tue, 7 May 2024 10:53:59 +0000 Subject: [PATCH 011/215] Fix construction instructions on flippables (#27574) Fixes #27547 --- .../Entities/Structures/Piping/Atmospherics/trinary.yml | 5 +++++ .../Entities/Structures/Piping/Disposal/pipes.yml | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml index 46eb57c388d..e8025556aa5 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml @@ -96,6 +96,9 @@ enabled: True: { state: gasFilterFOn } False: { state: gasFilterF } + - type: Construction + node: filterflipped + - type: PipeColorVisuals - type: NodeContainer nodes: @@ -196,6 +199,8 @@ !type:PipeNode nodeGroupID: Pipe pipeDirection: North + - type: Construction + node: mixerflipped - type: entity parent: GasPipeBase diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml index 02241051179..7aee5896472 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml @@ -271,6 +271,8 @@ bounds: "-0.25,-0.5,0.5,0.5" mask: - SubfloorMask + - type: Construction + node: routerflipped - type: entity id: DisposalJunction @@ -353,6 +355,8 @@ bounds: "-0.25,-0.5,0.5,0.5" mask: - SubfloorMask + - type: Construction + node: junctionflipped - type: entity id: DisposalYJunction @@ -489,3 +493,5 @@ pipe: Free: { state: signal-router-flipped-free } Anchored: { state: signal-router-flipped } + - type: Construction + node: signal_router_flipped From b0369388bd035c4840a5fcf79d821b4975db26b1 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 7 May 2024 08:13:28 -0700 Subject: [PATCH 012/215] Log event starts to admin alert chat (#27761) --- Content.Server/StationEvents/EventManagerSystem.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Content.Server/StationEvents/EventManagerSystem.cs b/Content.Server/StationEvents/EventManagerSystem.cs index d96d3fa7299..26fbde9b3ff 100644 --- a/Content.Server/StationEvents/EventManagerSystem.cs +++ b/Content.Server/StationEvents/EventManagerSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Server.Chat.Managers; using Content.Server.GameTicking; using Content.Server.StationEvents.Components; using Content.Shared.CCVar; @@ -16,6 +17,7 @@ public sealed class EventManagerSystem : EntitySystem [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IChatManager _chat = default!; [Dependency] public readonly GameTicker GameTicker = default!; [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; //Nyano - Summary: pulls in the glimmer system. @@ -45,6 +47,7 @@ public string RunRandomEvent() var ent = GameTicker.AddGameRule(randomEvent); var str = Loc.GetString("station-event-system-run-event",("eventName", ToPrettyString(ent))); + _chat.SendAdminAlert(str); Log.Info(str); return str; } From 9534d852667c6feca72b0b2002ad12f0c2bcabc5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 7 May 2024 15:14:35 +0000 Subject: [PATCH 013/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index af0ce0e4ef9..bc73e61291d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix chat bubbles. - type: Fix - id: 6046 - time: '2024-02-27T13:01:24.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25643 - author: rosieposieeee changes: - message: Added grey stalagmites and made all stalagmites weaker and more fun to @@ -3856,3 +3849,10 @@ id: 6545 time: '2024-05-07T07:26:33.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27750 +- author: mirrorcult + changes: + - message: Events that run now get logged to admin alert chat + type: Tweak + id: 6546 + time: '2024-05-07T15:13:29.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27761 From d6cc392388b577ae87625e6654320e5f365b16d6 Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Tue, 7 May 2024 08:33:00 -0700 Subject: [PATCH 014/215] Set max line width to 120 (#27765) --- .editorconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.editorconfig b/.editorconfig index f01530bcb18..3e44d1a2811 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,6 +12,7 @@ tab_width = 4 #end_of_line = crlf insert_final_newline = true trim_trailing_whitespace = true +max_line_length = 120 #### .NET Coding Conventions #### From d57b400d74155c1fe973954178a9a8ace5c047ed Mon Sep 17 00:00:00 2001 From: cool dolphin <36332236+Afrokada@users.noreply.github.com> Date: Tue, 7 May 2024 17:55:03 +0200 Subject: [PATCH 015/215] More trinkets for loadouts (#27292) * woo yea more tinket * plushie has no timer --- .../Loadouts/Miscellaneous/trinkets.yml | 99 +++++++++++++++++++ .../Prototypes/Loadouts/loadout_groups.yml | 8 ++ 2 files changed, 107 insertions(+) diff --git a/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml b/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml index 889b12a08e5..fe826a83340 100644 --- a/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml +++ b/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml @@ -1,3 +1,14 @@ +# Timers +- type: loadoutEffectGroup + id: Command + effects: + - !type:JobRequirementLoadoutEffect + requirement: + !type:DepartmentTimeRequirement + department: Command + time: 3600 # 1 hour + +# Plushies - type: loadout id: PlushieLizard equipment: PlushieLizard @@ -7,7 +18,95 @@ storage: back: - PlushieLizard + +- type: loadout + id: PlushieSpaceLizard + equipment: PlushieSpaceLizard + +- type: startingGear + id: PlushieSpaceLizard + storage: + back: + - PlushieSpaceLizard + +# Smokeables +- type: loadout + id: Lighter + equipment: Lighter + +- type: startingGear + id: Lighter + storage: + back: + - Lighter + +- type: loadout + id: CigPackGreen + equipment: CigPackGreen + +- type: startingGear + id: CigPackGreen + storage: + back: + - CigPackGreen + +- type: loadout + id: CigPackRed + equipment: CigPackRed + +- type: startingGear + id: CigPackRed + storage: + back: + - CigPackRed + +- type: loadout + id: CigPackBlue + equipment: CigPackBlue + +- type: startingGear + id: CigPackBlue + storage: + back: + - CigPackBlue + +- type: loadout + id: CigPackBlack + equipment: CigPackBlack + +- type: startingGear + id: CigPackBlack + storage: + back: + - CigPackBlack + +- type: loadout + id: CigarCase + equipment: CigarCase + effects: + - !type:GroupLoadoutEffect + proto: Command + +- type: startingGear + id: CigarCase + storage: + back: + - CigarCase + +- type: loadout + id: CigarGold + equipment: CigarGold + effects: + - !type:GroupLoadoutEffect + proto: Command + +- type: startingGear + id: CigarGold + storage: + back: + - CigarGold +# Pins - type: loadout id: ClothingNeckLGBTPin equipment: ClothingNeckLGBTPin diff --git a/Resources/Prototypes/Loadouts/loadout_groups.yml b/Resources/Prototypes/Loadouts/loadout_groups.yml index 299a3091e73..2205e28cc6b 100644 --- a/Resources/Prototypes/Loadouts/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/loadout_groups.yml @@ -6,6 +6,14 @@ maxLimit: 3 loadouts: - PlushieLizard + - PlushieSpaceLizard + - Lighter + - CigPackGreen + - CigPackRed + - CigPackBlue + - CigPackBlack + - CigarCase + - CigarGold - ClothingNeckLGBTPin - ClothingNeckAromanticPin - ClothingNeckAsexualPin From 03e0c19dcf40ed084c4a138909d9c81fa2f2e018 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 7 May 2024 15:56:09 +0000 Subject: [PATCH 016/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index bc73e61291d..8b8322cae9b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: rosieposieeee - changes: - - message: Added grey stalagmites and made all stalagmites weaker and more fun to - break. - type: Add - id: 6047 - time: '2024-02-27T19:24:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25646 - author: metalgearsloth changes: - message: Made NPC movement less janky. @@ -3856,3 +3848,10 @@ id: 6546 time: '2024-05-07T15:13:29.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27761 +- author: cooldolphin + changes: + - message: Cigarettes in the loadout menu. + type: Add + id: 6547 + time: '2024-05-07T15:55:03.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27292 From b0e86aa103b3d46882e234636161c07eb6cbb7f1 Mon Sep 17 00:00:00 2001 From: Hannah Giovanna Dawson Date: Tue, 7 May 2024 20:14:58 +0200 Subject: [PATCH 017/215] Weapon Reflection Movement Mechanic (#27219) * Weapon Reflection Movement Mechanic Adds a movement mechanic to deflection. Standing still gives you your best chance of deflecting a shot. Moving lowers this to 2/3rds. Sprinting to 1/3rd. This allows for robust players to express better and provides counterplay to someone finding a goober-strong deflection weapon, giving more design space. As part of this PR I've also touched the numbers of a few swords, shields, etc. and modified some descriptions to make them read better. The balance numbers are not remotely final, but as intent: 1. All the sidearm swords (katana, cutlass, captain's sabre) have the same damage. There's no good reason the "ceremonial" blade the captain has doing more damage than a katana. 2. The Captain's Sabre has a 30% reflect chance, dropping to 20% when moving and 10% when sprinting. This one is controversial due to the recent nerf, I suspect: This could easily be 15->10->5? 3. The Energy Katana has a flat 30% reflect chance. 4. The meme Throngler has a 30% reflect chance, dropping to 20% when moving and 10% when sprinting. 5. The E-Sword has a 30% reflect chance, dropping to 20% when moving and 10% when sprinting. 6. The Double E-Sword has a mighty 75% reflect chance, dropping to 50% and then 25%. 7. Both reflective shields - Mirror and Energy - have a 95% deflect chance, dropping to 63% then 31%. * Resolve PR comments. * Weh? * Reign in double esword a tad * Shield nerfs no longer real * Improve Mirror Cult desc * Simple alert for deflection! No art yet. * Added a new icon for deflecting --- Content.Shared/Alert/AlertType.cs | 3 +- .../Weapons/Reflect/ReflectComponent.cs | 35 ++++++-- .../Weapons/Reflect/ReflectSystem.cs | 84 ++++++++++++++++-- Resources/Locale/en-US/alerts/alerts.ftl | 3 + Resources/Prototypes/Alerts/alerts.yml | 9 ++ Resources/Prototypes/Anomaly/behaviours.yml | 1 + .../Entities/Clothing/OuterClothing/armor.yml | 1 + .../Entities/Mobs/NPCs/hellspawn.yml | 1 + .../Entities/Objects/Shields/shields.yml | 4 +- .../Objects/Weapons/Melee/e_sword.yml | 7 +- .../Entities/Objects/Weapons/Melee/sword.yml | 21 +++-- .../Alerts/deflecting.rsi/deflecting0.png | Bin 0 -> 1761 bytes .../Interface/Alerts/deflecting.rsi/meta.json | 14 +++ 13 files changed, 162 insertions(+), 21 deletions(-) create mode 100644 Resources/Textures/Interface/Alerts/deflecting.rsi/deflecting0.png create mode 100644 Resources/Textures/Interface/Alerts/deflecting.rsi/meta.json diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs index b917dd692d7..b989b8d4b6f 100644 --- a/Content.Shared/Alert/AlertType.cs +++ b/Content.Shared/Alert/AlertType.cs @@ -52,7 +52,8 @@ public enum AlertType : byte SuitPower, BorgHealth, BorgCrit, - BorgDead + BorgDead, + Deflecting } } diff --git a/Content.Shared/Weapons/Reflect/ReflectComponent.cs b/Content.Shared/Weapons/Reflect/ReflectComponent.cs index 8e7b8975d9d..5d8432ac776 100644 --- a/Content.Shared/Weapons/Reflect/ReflectComponent.cs +++ b/Content.Shared/Weapons/Reflect/ReflectComponent.cs @@ -21,17 +21,42 @@ public sealed partial class ReflectComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField("reflects")] public ReflectType Reflects = ReflectType.Energy | ReflectType.NonEnergy; + [DataField("spread"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public Angle Spread = Angle.FromDegrees(45); + + [DataField("soundOnReflect")] + public SoundSpecifier? SoundOnReflect = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg"); + /// - /// Probability for a projectile to be reflected. + /// Is the deflection an innate power or something actively maintained? If true, this component grants a flat + /// deflection chance rather than a chance that degrades when moving/weightless/stunned/etc. + /// + [DataField] + public bool Innate = false; + + /// + /// Maximum probability for a projectile to be reflected. /// [DataField("reflectProb"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public float ReflectProb = 0.25f; - [DataField("spread"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public Angle Spread = Angle.FromDegrees(45); + /// + /// The maximum velocity a wielder can move at before losing effectiveness. + /// + [DataField] + public float VelocityBeforeNotMaxProb = 2.5f; // Walking speed for a human. Suitable for a weightless deflector like an e-sword. - [DataField("soundOnReflect")] - public SoundSpecifier? SoundOnReflect = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg"); + /// + /// The velocity a wielder has to be moving at to use the minimum effectiveness value. + /// + [DataField] + public float VelocityBeforeMinProb = 4.5f; // Sprinting speed for a human. Suitable for a weightless deflector like an e-sword. + + /// + /// Minimum probability for a projectile to be reflected. + /// + [DataField] + public float MinReflectProb = 0.1f; } [Flags] diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 014b3cfe1ff..36dbedb4cb1 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -1,17 +1,20 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using Content.Shared.Administration.Logs; +using Content.Shared.Alert; using Content.Shared.Audio; +using Content.Shared.Damage.Components; using Content.Shared.Database; +using Content.Shared.Gravity; using Content.Shared.Hands; using Content.Shared.Inventory; using Content.Shared.Inventory.Events; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Popups; using Content.Shared.Projectiles; +using Content.Shared.Standing; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; using Robust.Shared.Physics.Components; @@ -35,6 +38,9 @@ public sealed class ReflectSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; + [Dependency] private readonly AlertsSystem _alerts = default!; public override void Initialize() { @@ -91,15 +97,20 @@ private void OnReflectCollide(EntityUid uid, ReflectComponent component, ref Pro private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid projectile, ProjectileComponent? projectileComp = null, ReflectComponent? reflect = null) { - if (!Resolve(reflector, ref reflect, false) || + // Do we have the components needed to try a reflect at all? + if ( + !Resolve(reflector, ref reflect, false) || !reflect.Enabled || !TryComp(projectile, out var reflective) || (reflect.Reflects & reflective.Reflective) == 0x0 || - !_random.Prob(reflect.ReflectProb) || - !TryComp(projectile, out var physics)) - { + !TryComp(projectile, out var physics) || + TryComp(reflector, out var staminaComponent) && staminaComponent.Critical || + _standing.IsDown(reflector) + ) + return false; + + if (!_random.Prob(CalcReflectChance(reflector, reflect))) return false; - } var rotation = _random.NextAngle(-reflect.Spread / 2, reflect.Spread / 2).Opposite(); var existingVelocity = _physics.GetMapLinearVelocity(projectile, component: physics); @@ -137,6 +148,34 @@ private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid return true; } + private float CalcReflectChance(EntityUid reflector, ReflectComponent reflect) + { + /* + * The rules of deflection are as follows: + * If you innately reflect things via magic, biology etc., you always have a full chance. + * If you are standing up and standing still, you're prepared to deflect and have full chance. + * If you have velocity, your deflection chance depends on your velocity, clamped. + * If you are floating, your chance is the minimum value possible. + * You cannot deflect if you are knocked down or stunned. + */ + + if (reflect.Innate) + return reflect.ReflectProb; + + if (_gravity.IsWeightless(reflector)) + return reflect.MinReflectProb; + + if (!TryComp(reflector, out var reflectorPhysics)) + return reflect.ReflectProb; + + return MathHelper.Lerp( + reflect.MinReflectProb, + reflect.ReflectProb, + // Inverse progression between velocities fed in as progression between probabilities. We go high -> low so the output here needs to be _inverted_. + 1 - Math.Clamp((reflectorPhysics.LinearVelocity.Length() - reflect.VelocityBeforeNotMaxProb) / (reflect.VelocityBeforeMinProb - reflect.VelocityBeforeNotMaxProb), 0, 1) + ); + } + private void OnReflectHitscan(EntityUid uid, ReflectComponent component, ref HitScanReflectAttemptEvent args) { if (args.Reflected || @@ -162,7 +201,14 @@ private bool TryReflectHitscan( { if (!TryComp(reflector, out var reflect) || !reflect.Enabled || - !_random.Prob(reflect.ReflectProb)) + TryComp(reflector, out var staminaComponent) && staminaComponent.Critical || + _standing.IsDown(reflector)) + { + newDirection = null; + return false; + } + + if (!_random.Prob(CalcReflectChance(reflector, reflect))) { newDirection = null; return false; @@ -191,6 +237,9 @@ private void OnReflectEquipped(EntityUid uid, ReflectComponent component, GotEqu return; EnsureComp(args.Equipee); + + if (component.Enabled) + EnableAlert(args.Equipee); } private void OnReflectUnequipped(EntityUid uid, ReflectComponent comp, GotUnequippedEvent args) @@ -204,6 +253,9 @@ private void OnReflectHandEquipped(EntityUid uid, ReflectComponent component, Go return; EnsureComp(args.User); + + if (component.Enabled) + EnableAlert(args.User); } private void OnReflectHandUnequipped(EntityUid uid, ReflectComponent component, GotUnequippedHandEvent args) @@ -215,6 +267,11 @@ private void OnToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggl { comp.Enabled = args.Activated; Dirty(uid, comp); + + if (comp.Enabled) + EnableAlert(uid); + else + DisableAlert(uid); } /// @@ -228,9 +285,22 @@ private void RefreshReflectUser(EntityUid user) continue; EnsureComp(user); + EnableAlert(user); + return; } RemCompDeferred(user); + DisableAlert(user); + } + + private void EnableAlert(EntityUid alertee) + { + _alerts.ShowAlert(alertee, AlertType.Deflecting); + } + + private void DisableAlert(EntityUid alertee) + { + _alerts.ClearAlert(alertee, AlertType.Deflecting); } } diff --git a/Resources/Locale/en-US/alerts/alerts.ftl b/Resources/Locale/en-US/alerts/alerts.ftl index 319809da40a..24bc60cbf15 100644 --- a/Resources/Locale/en-US/alerts/alerts.ftl +++ b/Resources/Locale/en-US/alerts/alerts.ftl @@ -107,3 +107,6 @@ alerts-revenant-essence-desc = The power of souls. It sustains you and is used f alerts-revenant-corporeal-name = Corporeal alerts-revenant-corporeal-desc = You have manifested physically. People around you can see and hurt you. + +alerts-deflecting-name = Deflecting +alerts-deflecting-desc = You have a chance to deflect incoming projectiles. Standing still or moving slowly will increase this chance. diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index e9a7f9c9584..7881cddd4aa 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -24,6 +24,7 @@ - category: Thirst - alertType: Magboots - alertType: Pacified + - alertType: Deflecting - type: entity id: AlertSpriteView @@ -474,3 +475,11 @@ state: critical name: Debug6 description: Debug + +- type: alert + id: Deflecting + icons: + - sprite: /Textures/Interface/Alerts/deflecting.rsi + state: deflecting0 + name: alerts-deflecting-name + description: alerts-deflecting-desc diff --git a/Resources/Prototypes/Anomaly/behaviours.yml b/Resources/Prototypes/Anomaly/behaviours.yml index dea1ddb69c3..e39933c365c 100644 --- a/Resources/Prototypes/Anomaly/behaviours.yml +++ b/Resources/Prototypes/Anomaly/behaviours.yml @@ -84,6 +84,7 @@ description: anomaly-behavior-reflect components: - type: Reflect + innate: true reflectProb: 0.5 reflects: - Energy diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index ecc4156affa..6da428ee5f6 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -96,6 +96,7 @@ Heat: 0.4 # this technically means it protects against fires pretty well? -heat is just for lasers and stuff, not atmos temperature - type: Reflect reflectProb: 1 + innate: true # armor grants a passive shield that does not require concentration to maintain reflects: - Energy diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/hellspawn.yml b/Resources/Prototypes/Entities/Mobs/NPCs/hellspawn.yml index 26fbe4e0734..74658f0a2db 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/hellspawn.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/hellspawn.yml @@ -55,6 +55,7 @@ - type: Perishable - type: Reflect reflectProb: 0.7 + innate: true reflects: - Energy - type: Fixtures diff --git a/Resources/Prototypes/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Entities/Objects/Shields/shields.yml index b794e42ff7d..e7ebb1b98d4 100644 --- a/Resources/Prototypes/Entities/Objects/Shields/shields.yml +++ b/Resources/Prototypes/Entities/Objects/Shields/shields.yml @@ -313,7 +313,7 @@ name: mirror shield parent: BaseShield id: MirrorShield - description: Eerily glows red... you hear the geometer whispering + description: Glows an eerie red. You hear the Geometer whispering... components: - type: Sprite state: mirror-icon @@ -321,6 +321,7 @@ heldPrefix: mirror - type: Reflect reflectProb: 0.95 + innate: true reflects: - Energy - type: Blocking #Mirror shield reflects heat/laser, but is relatively weak to everything else. @@ -408,6 +409,7 @@ - type: Reflect enabled: false reflectProb: 0.95 + innate: true reflects: - Energy - type: Blocking diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index bc376df5eab..dcf65d03827 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -78,6 +78,8 @@ malus: 0 - type: Reflect enabled: false + reflectProb: 0.5 + minReflectProb: 0.25 - type: IgnitionSource temperature: 700 @@ -225,7 +227,7 @@ name: double-bladed energy sword parent: EnergySword id: EnergySwordDouble - description: Syndicate Command Interns thought that having one blade on the energy sword was not enough. This can be stored in pockets. + description: Syndicate Command's intern thought that having only one blade on energy swords was not cool enough. This can be stored in pockets. components: - type: EnergySword - type: ItemToggle @@ -276,7 +278,8 @@ size: Small sprite: Objects/Weapons/Melee/e_sword_double-inhands.rsi - type: Reflect - reflectProb: .80 #DeltaV: 80% Energy Reflection but no ballistics. + reflectProb: .80 + minReflectProb: .65 spread: 75 reflects: - Energy #DeltaV: 80% Energy Reflection but no ballistics. diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 5ffe16d3b53..dd41468892c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -13,12 +13,17 @@ attackRate: 1.5 damage: types: - Slash: 17 #cmon, it has to be at least BETTER than the rest. + Slash: 15 soundHit: path: /Audio/Weapons/bladeslice.ogg - type: Reflect enabled: true - reflectProb: .1 + # Design intent: a robust captain or tot can sacrifice movement to make the most of this weapon, but they have to + # really restrict themselves to walking speed or less. + reflectProb: 0.5 + velocityBeforeNotMaxProb: 1.0 + velocityBeforeMinProb: 3.0 + minReflectProb: 0.1 spread: 90 - type: Item size: Normal @@ -83,6 +88,9 @@ - Back - Belt - type: Reflect + reflectProb: 0.3 + velocityBeforeNotMaxProb: 6.0 # don't punish ninjas for being ninjas + velocityBeforeMinProb: 10.0 - type: entity name: machete @@ -152,7 +160,7 @@ wideAnimationRotation: -135 damage: types: - Slash: 16 + Slash: 15 soundHit: path: /Audio/Weapons/bladeslice.ogg - type: Item @@ -164,7 +172,7 @@ name: The Throngler parent: BaseItem id: Throngler - description: Why would you make this? + description: Why would someone make this? components: - type: Sharp - type: Sprite @@ -185,7 +193,10 @@ path: /Audio/Effects/explosion_small1.ogg - type: Reflect enabled: true - reflectProb: .25 + reflectProb: 0.5 # In robust hands, deflects as well as an e-sword + velocityBeforeNotMaxProb: 1.0 + velocityBeforeMinProb: 3.0 + minReflectProb: 0.1 spread: 90 - type: Item size: Ginormous diff --git a/Resources/Textures/Interface/Alerts/deflecting.rsi/deflecting0.png b/Resources/Textures/Interface/Alerts/deflecting.rsi/deflecting0.png new file mode 100644 index 0000000000000000000000000000000000000000..37404e77f76444946eaa481b8f4ec861f85736c6 GIT binary patch literal 1761 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}e5nzhX}-P; zT0k}j5QD&_;K@Lev%n*=n1MmW7law7oa)tQU|_Dx42dX-@b$4u&d=3LOvz75)vL%Y z0PC`;umUo3Q%e#RDspr3imfVamB1>jfNYSkzLEl1NlCV?QiN}Sf^&XRs)CuGfu4bq z9hZWFf=y9MnpKdC8&o@xXRDM^Qc_^0uU}qXu2*iXmtT~wZ)j<0sc&GUZ)BtkRH0j3 znOBlnp_^B%3^4>|j!SBBa#3bMNoIbY0?6FNr2NtnTO}osMQ{LdXG${Mo`TY%9I!1Z z$@-}|sky0nCB^!NdWQPg^p#|$AzYYO3=Ixo!03ZyfZ7bOYV#~8Nj3q7lxqdhJy8Dv z9hwZbx40xlA4!3}k%57Qu7Q!Rk)=M|e?aHkq$FFFWR~Qlf&&ijA8-gd=9Hj{g4Bb8 zASV+PvQ{~XdFi%F6}l;@X^EvdB}#Uod0?Yb6da36%JYk|ZS*0kQB8q}q8e_akHsA} zAm3X>2Bj9~=ahoN-_Fq3$OarHD58j%far+8ssmXRT}MDhen~zsWff&6d*+p-78Mi$ zQyJJsn0>fapqquTJTxz}#13WvnlO?sq*$_o23!a@MlgdDr&6eKkf23Mv5@Qljs{?U zu;a4PhvzLjuD|Ez{a|2VdgSTi7!twxHf(>ll%q)9>g)3aH%&4L?C@0f=sKY3^-_5x?PXMg=&dwlOg7bU|>CT~x9 zb_T6CWpZ@TUF_5_Lklimk-JuS7}Ko>mE&-XwT%@ zz~AnCsWjGS_c!$k_qyy%=bv{x)ZwDkSM>CArNyI_f|-FzCbQ0}DK0pe;IJ{`jA%$K z$3xdA@?HlRJyy6KS>*1mHaTQ%m}f)%-=FRGo-dZ+d604WWl4U)jbbK=2$gdI!WH>j zqh6HV`q?dPS6*4`o*(~j>3RD+3qv0!n~cdU8!k@z*R#?f>E!~ag&Ur}|8vr7UvkaQ z=;b+Pr=RmN9(OaAd3jBxQDi~KqKg_A7l)U0m1^@YVz=nh@z^ISVL63sg*fk&6Deo1 z6F1+ypsT#ltU+0H_0+W;yW~=II3G+hT(|GUqiho?6S@AK`V$0rA1qp_DOl|Id#y;u z%5t^N%geXT-<=&@XLj20rd8Jjj+DHd=yt)~@iJ*|HCHywVB%a6_`e{Ld3DQ-g`9e2 zJHC5NO!Nqp7KGu&fThp?^U)%zxfj8D>H6YSo*o)!HwtY_Bk?~ z3617rwSO<0-|6{uH|pctX{S|7cgLQ8@i3*mA^1zdvC9PwTy;L%{TO><_x)_sd|sTk z@bjThlf;icpI+^l)7}u_&|l5Ce9_&!$2uEl8U5J%o_$~c7pCKr88jzx3*G2gzbKlU ziBEnW%e&>y6Ao;Tv%SAUaD|Y_o#_q1Q<_}#rY|nA(CNRW_~66)T7N6U4bP6eznPTy lZ%sw4nOvUSG&zw4^;={Tb>7Fkn+K|1JYD@<);T3K0RY#fl2ZTx literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/deflecting.rsi/meta.json b/Resources/Textures/Interface/Alerts/deflecting.rsi/meta.json new file mode 100644 index 00000000000..f5d94c891a7 --- /dev/null +++ b/Resources/Textures/Interface/Alerts/deflecting.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Deflecting icon by Ubaser", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "deflecting0" + } + ] +} From f7b243ae04f06d4256814d7f83b21d58a7a7e187 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 7 May 2024 18:16:04 +0000 Subject: [PATCH 018/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 8b8322cae9b..fe201e3a1fe 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Made NPC movement less janky. - type: Fix - id: 6048 - time: '2024-02-28T06:41:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25666 - author: musicmanvr changes: - message: Added the Syndicate decoy bundle, a creative way to trick your foes. @@ -3855,3 +3848,12 @@ id: 6547 time: '2024-05-07T15:55:03.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27292 +- author: FairlySadPanda + changes: + - message: Weapons that deflect shots, like the e-sword, now are most effective + at doing so when standing still, with moving or sprinting making the deflection + chance worse. + type: Tweak + id: 6548 + time: '2024-05-07T18:14:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27219 From 51d94f2022a3feadd85f585ca397bfa7284ea87c Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Tue, 7 May 2024 18:20:43 +0000 Subject: [PATCH 019/215] make lube speed up lathes (#25515) * add LatheGetSpeedEvent * add LatheLube system * make typical lathes accept lube * spill * :trollface: * rework to generic ReagentSpeedSystem * hyperlathe ops --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- Content.Server/Lathe/LatheSystem.cs | 6 +- .../ReagentSpeed/ReagentSpeedComponent.cs | 34 +++++++++++ .../ReagentSpeed/ReagentSpeedSystem.cs | 33 ++++++++++ .../Entities/Structures/Machines/lathe.yml | 61 +++++++++++++------ 4 files changed, 116 insertions(+), 18 deletions(-) create mode 100644 Content.Shared/ReagentSpeed/ReagentSpeedComponent.cs create mode 100644 Content.Shared/ReagentSpeed/ReagentSpeedSystem.cs diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index 2b3b810fba7..7448a9b84dd 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -14,6 +14,7 @@ using Content.Shared.Emag.Components; using Content.Shared.Lathe; using Content.Shared.Materials; +using Content.Shared.ReagentSpeed; using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; using JetBrains.Annotations; @@ -35,6 +36,7 @@ public sealed class LatheSystem : SharedLatheSystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly UserInterfaceSystem _uiSys = default!; [Dependency] private readonly MaterialStorageSystem _materialStorage = default!; + [Dependency] private readonly ReagentSpeedSystem _reagentSpeed = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly TransformSystem _transform = default!; @@ -186,9 +188,11 @@ public bool TryStartProducing(EntityUid uid, LatheComponent? component = null) var recipe = component.Queue.First(); component.Queue.RemoveAt(0); + var time = _reagentSpeed.ApplySpeed(uid, recipe.CompleteTime); + var lathe = EnsureComp(uid); lathe.StartTime = _timing.CurTime; - lathe.ProductionLength = recipe.CompleteTime * component.TimeMultiplier; + lathe.ProductionLength = time * component.TimeMultiplier; component.CurrentRecipe = recipe; var ev = new LatheStartPrintingEvent(recipe); diff --git a/Content.Shared/ReagentSpeed/ReagentSpeedComponent.cs b/Content.Shared/ReagentSpeed/ReagentSpeedComponent.cs new file mode 100644 index 00000000000..d233cad2a00 --- /dev/null +++ b/Content.Shared/ReagentSpeed/ReagentSpeedComponent.cs @@ -0,0 +1,34 @@ +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; + +namespace Content.Shared.ReagentSpeed; + +/// +/// Makes a device work faster by consuming reagents on each use. +/// Other systems must use for this to do anything. +/// +[RegisterComponent, Access(typeof(ReagentSpeedSystem))] +public sealed partial class ReagentSpeedComponent : Component +{ + /// + /// Solution that will be checked. + /// Anything that isn't in Modifiers is left alone. + /// + [DataField(required: true)] + public string Solution = string.Empty; + + /// + /// How much reagent from the solution to use up for each use. + /// This is per-modifier-reagent and not shared between them. + /// + [DataField] + public FixedPoint2 Cost = 5; + + /// + /// Reagents and how much they modify speed at full purity. + /// Small number means faster large number means slower. + /// + [DataField(required: true)] + public Dictionary, float> Modifiers = new(); +} diff --git a/Content.Shared/ReagentSpeed/ReagentSpeedSystem.cs b/Content.Shared/ReagentSpeed/ReagentSpeedSystem.cs new file mode 100644 index 00000000000..8561c7b12a1 --- /dev/null +++ b/Content.Shared/ReagentSpeed/ReagentSpeedSystem.cs @@ -0,0 +1,33 @@ +using Content.Shared.Chemistry.EntitySystems; + +namespace Content.Shared.ReagentSpeed; + +public sealed class ReagentSpeedSystem : EntitySystem +{ + [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; + + /// + /// Consumes reagents and modifies the duration. + /// This can be production time firing delay etc. + /// + public TimeSpan ApplySpeed(Entity ent, TimeSpan time) + { + if (!Resolve(ent, ref ent.Comp, false)) + return time; + + if (!_solution.TryGetSolution(ent.Owner, ent.Comp.Solution, out _, out var solution)) + return time; + + foreach (var (reagent, fullModifier) in ent.Comp.Modifiers) + { + var used = solution.RemoveReagent(reagent, ent.Comp.Cost); + var efficiency = (used / ent.Comp.Cost).Float(); + // scale the speed modifier so microdosing has less effect + var reduction = (1f - fullModifier) * efficiency; + var modifier = 1f - reduction; + time *= modifier; + } + + return time; + } +} diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 235a998c190..7b5e7362353 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -50,9 +50,44 @@ - type: ResearchClient - type: TechnologyDatabase +# a lathe that can be sped up with space lube / slowed down with glue - type: entity - id: Autolathe + abstract: true parent: BaseLathe + id: BaseLatheLube + components: + - type: ReagentSpeed + solution: lube + modifiers: + SpaceLube: 0.25 + SpaceGlue: 5 + - type: SolutionContainerManager + solutions: + lube: + maxVol: 250 + - type: Spillable + solution: lube + - type: RefillableSolution + solution: lube + - type: ExaminableSolution + solution: lube + +- type: entity + abstract: true + id: BaseHyperlathe + components: + - type: Lathe + materialUseMultiplier: 0.5 + timeMultiplier: 1.5 + - type: LatheHeatProducing + - type: ReagentSpeed + modifiers: + SpaceLube: 0.8 # being faster means less heat so lube needs to be nerfed + SpaceGlue: 5 # no change from normal lathe, overheat!!! + +- type: entity + id: Autolathe + parent: BaseLatheLube name: autolathe description: It produces items using metal and glass. components: @@ -226,22 +261,18 @@ - type: entity id: AutolatheHyperConvection - parent: Autolathe + parent: [Autolathe, BaseHyperlathe] name: hyper convection autolathe description: A highly-experimental autolathe that harnesses the power of extreme heat to slowly create objects more cost-effectively. components: - type: Sprite sprite: Structures/Machines/autolathe_hypercon.rsi - - type: Lathe - materialUseMultiplier: 0.5 - timeMultiplier: 1.5 - - type: LatheHeatProducing - type: Machine board: AutolatheHyperConvectionMachineCircuitboard - type: entity id: Protolathe - parent: BaseLathe + parent: BaseLatheLube name: protolathe description: Converts raw materials into useful objects. components: @@ -351,22 +382,18 @@ - type: entity id: ProtolatheHyperConvection - parent: Protolathe + parent: [Protolathe, BaseHyperlathe] name: hyper convection protolathe description: A highly-experimental protolathe that harnesses the power of extreme heat to slowly create objects more cost-effectively. components: - type: Sprite sprite: Structures/Machines/protolathe_hypercon.rsi - - type: Lathe - materialUseMultiplier: 0.5 - timeMultiplier: 1.5 - - type: LatheHeatProducing - type: Machine board: ProtolatheHyperConvectionMachineCircuitboard - type: entity id: CircuitImprinter - parent: BaseLathe + parent: BaseLatheLube name: circuit imprinter description: Prints circuit boards for machines. components: @@ -516,7 +543,7 @@ - type: entity id: ExosuitFabricator - parent: BaseLathe + parent: BaseLatheLube name: exosuit fabricator description: Creates parts for robotics and other mechanical needs components: @@ -680,7 +707,7 @@ - type: entity id: SecurityTechFab - parent: BaseLathe + parent: BaseLatheLube name: security techfab description: Prints equipment for use by security crew. components: @@ -824,7 +851,7 @@ - type: entity id: AmmoTechFab - parent: BaseLathe + parent: BaseLatheLube name: ammo techfab description: Prints the bare minimum of bullets that any budget military or armory could need. Nothing fancy. components: @@ -872,7 +899,7 @@ - type: entity id: MedicalTechFab - parent: BaseLathe + parent: BaseLatheLube name: medical techfab description: Prints equipment for use by the medbay. components: From 673ed22525c46b55e6fef8f82a389c17bfce3e8c Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 7 May 2024 18:21:50 +0000 Subject: [PATCH 020/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index fe201e3a1fe..3ce8ccd5ec7 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,15 +1,4 @@ Entries: -- author: musicmanvr - changes: - - message: Added the Syndicate decoy bundle, a creative way to trick your foes. - type: Add - - message: Snap pops have been added to the toy crate. - type: Add - - message: Syndicate Commanders now have a trench whistle, Avanti! - type: Add - id: 6049 - time: '2024-02-28T21:53:46.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25333 - author: Whisper changes: - message: Iron and copper no longer grant infinite blood! @@ -3857,3 +3846,10 @@ id: 6548 time: '2024-05-07T18:14:59.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27219 +- author: deltanedas + changes: + - message: Lathes can now be sped up by using Space Lube. + type: Tweak + id: 6549 + time: '2024-05-07T18:20:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25515 From be2e38d40835e4d8c6273b5645fd9b89689aff2c Mon Sep 17 00:00:00 2001 From: TurboTracker <130304754+TurboTrackerss14@users.noreply.github.com> Date: Tue, 7 May 2024 20:44:20 +0100 Subject: [PATCH 021/215] Stop Toilets crushing you into walls (#27778) --- Resources/Prototypes/Entities/Structures/Furniture/toilet.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index 2556b9ddfde..a3124c09f6a 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -51,6 +51,10 @@ disposals: !type:Container - type: Physics bodyType: Static + - type: Fixtures + fixtures: + fix1: + hard: false - type: Construction graph: Toilet node: toilet From 1a8644d981550514cb1a2715ffdad55e1c1ccfdd Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Tue, 7 May 2024 20:42:28 +0000 Subject: [PATCH 022/215] make hyper printer inherit base lathe (#27777) Co-authored-by: deltanedas <@deltanedas:kde.org> --- Resources/Prototypes/Entities/Structures/Machines/lathe.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 7b5e7362353..91e65ba48db 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -528,16 +528,12 @@ - type: entity id: CircuitImprinterHyperConvection - parent: CircuitImprinter + parent: [CircuitImprinter, BaseHyperlathe] name: hyper convection circuit imprinter description: A highly-experimental circuit imprinter that harnesses the power of extreme heat to slowly create objects more cost-effectively. components: - type: Sprite sprite: Structures/Machines/circuit_imprinter_hypercon.rsi - - type: Lathe - materialUseMultiplier: 0.5 - timeMultiplier: 1.5 - - type: LatheHeatProducing - type: Machine board: CircuitImprinterHyperConvectionMachineCircuitboard From 4dfa5163669b4554b7aef87056698933e56f5341 Mon Sep 17 00:00:00 2001 From: OnsenCapy <101037138+LGRuthes@users.noreply.github.com> Date: Tue, 7 May 2024 21:19:55 -0300 Subject: [PATCH 023/215] Change combat gloves sprite (#27373) * Changed combat gloves sprite. * Edited combat gloves sprite. --- .../Entities/Clothing/Hands/gloves.yml | 6 ++++ .../Hands/Gloves/combat.rsi/equipped-HAND.png | Bin 0 -> 441 bytes .../Clothing/Hands/Gloves/combat.rsi/icon.png | Bin 0 -> 591 bytes .../Hands/Gloves/combat.rsi/inhand-left.png | Bin 0 -> 564 bytes .../Hands/Gloves/combat.rsi/inhand-right.png | Bin 0 -> 567 bytes .../Hands/Gloves/combat.rsi/meta.json | 26 ++++++++++++++++++ 6 files changed, 32 insertions(+) create mode 100644 Resources/Textures/Clothing/Hands/Gloves/combat.rsi/equipped-HAND.png create mode 100644 Resources/Textures/Clothing/Hands/Gloves/combat.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Hands/Gloves/combat.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Hands/Gloves/combat.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Hands/Gloves/combat.rsi/meta.json diff --git a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml index 0c2ded620eb..8b73eee0d24 100644 --- a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml +++ b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml @@ -242,6 +242,12 @@ name: combat gloves description: These tactical gloves are fireproof and shock resistant. components: + - type: Sprite + sprite: Clothing/Hands/Gloves/combat.rsi + - type: Clothing + sprite: Clothing/Hands/Gloves/combat.rsi + - type: GloveHeatResistance + heatResistance: 1400 - type: Insulated - type: Fiber fiberMaterial: fibers-insulative diff --git a/Resources/Textures/Clothing/Hands/Gloves/combat.rsi/equipped-HAND.png b/Resources/Textures/Clothing/Hands/Gloves/combat.rsi/equipped-HAND.png new file mode 100644 index 0000000000000000000000000000000000000000..7ca224617c0e6a82898ca646121b94dda88c2a8a GIT binary patch literal 441 zcmV;q0Y?6bP)Px$a!Eu%RCr$Pnn4P}Knz8vdqLENC`zx;`{?oPeR>5^6c;LjdnH0QrS?x+ZHLTv zo6bz~@?saA#6|u~BCp>O01b$o07oEl0r)4#5#R_!PJkm2xd40%a0EC4krUtuGzqX= zrLV<0b@SQ86-D7r$2|llYqBhJm+Re~4|}gQ-*V{V{KkUHVy5HKC&yi$Ljc>&<|WVG z9#3y*O#lSuKhO69APH&>(5yiKG<%gz^IZcwDjUjXKKhr1v{};x;h*P20M=g;1Yo_x zs6HBi2w9KQ0P7t__0a%C$aDbr zuI|o2-vG4t0jkES0mufw{Qj>Qfp5=t$)^>7UnCvM9t03dCx*&{1`I_$lyhl7D4iH8 j4;nBO`B2V{-hekD=C?uVRmpMy0000 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Hands/Gloves/combat.rsi/icon.png b/Resources/Textures/Clothing/Hands/Gloves/combat.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..eedef5290dbbb293635918f232f898977a718d48 GIT binary patch literal 591 zcmV-V0Px%2}wjjR9Hvtl-)}LK@`QW8MsohhW4d~P=Q}P2>$R z&M>RHXub%s^HMv`-1EC*wzOT+)o7i zv9k+3)5h-u*%o4J06&prI)xyfFPFHnuNf8C*VLa-Dhm;F|m%PMjHM+yg z0cLE1nGkelbsnsB0s9}8GIL&{)n5DT!&L#s+j$WA1%#kqP}fx2@Ud|e|0UQiX9Ix1 dfxv;lfnQV1WiT-mDvSUC002ovPDHLkV1oQr3V{Fs literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Hands/Gloves/combat.rsi/inhand-left.png b/Resources/Textures/Clothing/Hands/Gloves/combat.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..2e6ce0b9b9f63977587e93de8f8fc7812843f4d5 GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JRCr$Pnn6oKK@i91;TJG0q$q?86b*XdLAvw}BItt#QgraxrBmQVQK&;; zf~6ExP!PPBg$EWU`F6M2ZU67IymfYFe)FGw9wwr|pD6JB2>_sgzzLuQ0v7--L9_r` zAaDX`fxrcTuK-#AEf6>Xv_Rkjz*pc;TA;jBNp7wOnSJTnFH(8tpcWn4*gr^k|LXE0 z;&rjGLgVQpJw1%+Hz0KFmuYLG05&#DNgT(N%jHxIqx%VQ^8E0-Ubjo-?Gm-y?YVw+ z?UzYB{jD(o&1N%setl8rzQ27?rCL>0X8?nKUnM>q-Ko7=y-ugClP|t??Xx7$Hbwv( z9vvrKU4B*zd74b06o5{rqu%Y-D%9=uXfJNeZ3SKXtjQY#z?W2ht*EM<_BsPNKWjz2 zm&$(jKH&Qy4P4h=8~_Kv{ky(ZQWxL%wYm?6w<=-YOV_i3>)L09V59}II(NYi09>eo zWDHP%Bz+frLjf*SK{5s?K$5--zM%jYsvsEy6d+081>aDB3ssPe0Sb_$?}BeAz=bME z#sCFK(s#i(6yQP?Bx8UAB+Z+^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Hands/Gloves/combat.rsi/inhand-right.png b/Resources/Textures/Clothing/Hands/Gloves/combat.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..41ca0dddfb5127675137c8cb082dd3c4be81aba7 GIT binary patch literal 567 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%z-+Q_^hE&A8 zo#p7w6ew^!{Uf)8SdfSlTNj&d#RR?DW{zJA8bxGn%3g|81d6;8n315#DJsGtZM-2U z@Z%lPv}xb(U*7b7{ejPaI2j%=oabD?YGcmOrVz!T`@oWMOT%-v z1K|w14PqbWFhm_FW`3ak;0n{zyR{3*hHcbfGuTof3Bvn}YGfMwSrqNu^ z2EXOX=f7o(zWP>GB4FmT%}MGnQ$xE;-C~WaebsXrW8$_j#dYWYFO~aHv%%=Yr5Euk z2mfx~89eFW_s?<94t|LH_9?0+pVeWFc4wV&=#?f;hNvfNkF+wc332jfms#L?&nw2_ zfS2l)k^_vSN~}4;82PVWL9ik9-2JUuucgX089dM3k8RC(Tg*1$%b!?n!|OZz7%t7( zznkmgt>-KXXMgSvOIlYrm*K Date: Wed, 8 May 2024 00:21:01 +0000 Subject: [PATCH 024/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3ce8ccd5ec7..96c8b20e766 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Whisper - changes: - - message: Iron and copper no longer grant infinite blood! - type: Fix - id: 6050 - time: '2024-02-28T21:56:22.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25678 - author: Cojoke-dot changes: - message: E-sword now lights plasma fires @@ -3853,3 +3846,12 @@ id: 6549 time: '2024-05-07T18:20:44.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25515 +- author: OnsenCapy + changes: + - message: Combat gloves now have their own unique sprite. + type: Tweak + - message: Combat gloves now have heat resistance to match their fireproof description. + type: Tweak + id: 6550 + time: '2024-05-08T00:19:55.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27373 From dc4de10036abd14ff1d936926d370db1dfc6dc2b Mon Sep 17 00:00:00 2001 From: pigeonpeas <147350443+pigeonpeas@users.noreply.github.com> Date: Tue, 7 May 2024 20:22:25 -0400 Subject: [PATCH 025/215] Make the floppy lizard ears have two colors. (#27679) * Make the floppy lizard ears have two colors. * please fix whatever the hell happened * fix the error * suggestion from Ubaser * another suggestion from ubaser --- Resources/Locale/en-US/markings/reptilian.ftl | 3 ++- .../Mobs/Customization/Markings/reptilian.yml | 4 +++- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 1 - ...g => horns_floppy_kobold_ears_primary.png} | Bin 1717 -> 1696 bytes .../horns_floppy_kobold_ears_secondary.png | Bin 0 -> 197 bytes .../reptilian_parts.rsi/meta.json | 6 +++++- 6 files changed, 10 insertions(+), 4 deletions(-) rename Resources/Textures/Mobs/Customization/reptilian_parts.rsi/{horns_floppy_kobold_ears.png => horns_floppy_kobold_ears_primary.png} (74%) create mode 100644 Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears_secondary.png diff --git a/Resources/Locale/en-US/markings/reptilian.ftl b/Resources/Locale/en-US/markings/reptilian.ftl index 470af07361d..c2a2df4b3ba 100644 --- a/Resources/Locale/en-US/markings/reptilian.ftl +++ b/Resources/Locale/en-US/markings/reptilian.ftl @@ -96,7 +96,8 @@ marking-LizardHornsBighorn = Lizard Horns (Bighorn) marking-LizardHornsKoboldEars-horns_kobold_ears = Lizard Ears (Kobold) marking-LizardHornsKoboldEars = Lizard Ears (Kobold) -marking-LizardHornsFloppyKoboldEars-horns_floppy_kobold_ears = Lizard Ears (Floppy Kobold) +marking-LizardHornsFloppyKoboldEars-horns_floppy_kobold_ears_primary = Lizard Ears (Floppy Kobold) +marking-LizardHornsFloppyKoboldEars-horns-floppy_kobold_ears_secondary = Lizard Ears (Floppy Kobold) marking-LizardHornsFloppyKoboldEars = Lizard Ears (Floppy Kobold) marking-LizardChestUnderbelly-body_underbelly = Lizard Chest (Underbelly) diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml index 5a52e09bf2d..95cfb72e875 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml @@ -310,7 +310,9 @@ speciesRestriction: [Reptilian] sprites: - sprite: Mobs/Customization/reptilian_parts.rsi - state: horns_floppy_kobold_ears + state: horns_floppy_kobold_ears_primary + - sprite: Mobs/Customization/reptilian_parts.rsi + state: horns_floppy_kobold_ears_secondary - type: marking id: LizardChestUnderbelly diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 7fb431fabb5..6bc0d7a598f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1434,7 +1434,6 @@ horns_bighorn: KoboldHornColors horns_argali: KoboldHornColors horns_ayrshire: KoboldHornColors - horns_floppy_kobold_ears: Inherit horns_double: Inherit horns_kobold_ears: Inherit - type: Butcherable diff --git a/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears.png b/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears_primary.png similarity index 74% rename from Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears.png rename to Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears_primary.png index 061c0100cc5cc0d2216f0e25819a546421c7d300..89d7eaef817d3abd4702b58e71d92c1ffdda29b4 100644 GIT binary patch delta 321 zcmV-H0lxmV4WJFM-2plbP)t-sdU|@dx3{FEqyPW_*0rV`00004bW%=J|Ns90Kq{&9 z00009a7bBm001bK001bK0nAOV#j_g%kpq9@Nkl{xl= z5rPmfAqW8z0xNL#vcg~Ql*syhK<#EN;QiUd8`NrD1&;>qxfAFOw?Qv=C6Ei?*%sSn z1j|xgF#i`Ipm|*3F0|XxFe0j#lh1%UrL`w`gR^l4TaSl4LJ$Hb1R-F4EvWSapJ^^9 Tc?DQO00000NkvXXu0mjfcEE>} delta 343 zcmV-d0jU0<4Yduh-2pxiP)t-sdU|@dx3{FEq?DAD0000ux)%Ka000GaQchF<|Ns90 z`?d~K00009a7bBm001bN001bN0lI?#-?JA2kpqA9Nkl zFrv3a!%{BlYApt#?Mr)@E(BbJ;sIKlvI&s=(hFjD5M)i8U|9%IYhHo3Mh`sz<~!uK zXaIkpg~bG@=`sM=fodOgwGPheHNaB<0w4-N07L=U0ZuM2^5dP07QYW#vUvqaeyY9E z60IlTksvd70JS&r0qp{KW@?|>07s}E+P*IU!24&a8{%w7!f@QrZ~g{6*+#X%8@yW= p;pponPXP#kC;$Nv{Tfi~2lKN%Lkjt^{s0RA002ovPDHLkV1hgVk!An@ diff --git a/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears_secondary.png b/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears_secondary.png new file mode 100644 index 0000000000000000000000000000000000000000..b40d099eabd5c9969e85ab0a30ac36185fdccf56 GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sEXgD|57Yp@DXFv`=#F{C2y?IlCL1_KV(fG=}w?cu7{dLP*Y(}< Date: Wed, 8 May 2024 00:25:41 +0000 Subject: [PATCH 026/215] make dragons breathe fire (#26746) * add ActionGun system * add RepeatingTrigger * dragons breath projectile, repeatedly explodes * give dragon fire breathing action, fireproof it * oop * oop 2 * prevent troll * proper repeating thing * pro * webedit ops * realops --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Components/ExplosiveComponent.cs | 7 +++ .../Components/RepeatingTriggerComponent.cs | 25 +++++++++++ .../EntitySystems/ExplosionSystem.cs | 2 +- .../Explosion/EntitySystems/TriggerSystem.cs | 21 +++++++++ .../Ranged/Components/ActionGunComponent.cs | 37 +++++++++++++++ .../Weapons/Ranged/Systems/ActionGunSystem.cs | 41 +++++++++++++++++ .../Entities/Mobs/Player/dragon.yml | 45 +++++++++++++++++++ .../Weapons/Guns/Projectiles/magic.yml | 33 ++++++++++++++ 8 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 Content.Server/Explosion/Components/RepeatingTriggerComponent.cs create mode 100644 Content.Shared/Weapons/Ranged/Components/ActionGunComponent.cs create mode 100644 Content.Shared/Weapons/Ranged/Systems/ActionGunSystem.cs diff --git a/Content.Server/Explosion/Components/ExplosiveComponent.cs b/Content.Server/Explosion/Components/ExplosiveComponent.cs index 04a08955a35..2b27a89d9db 100644 --- a/Content.Server/Explosion/Components/ExplosiveComponent.cs +++ b/Content.Server/Explosion/Components/ExplosiveComponent.cs @@ -81,6 +81,13 @@ public sealed partial class ExplosiveComponent : Component [DataField("deleteAfterExplosion")] public bool? DeleteAfterExplosion; + /// + /// Whether to not set to true, allowing it to explode multiple times. + /// This should never be used if it is damageable. + /// + [DataField] + public bool Repeatable; + /// /// Avoid somehow double-triggering this explosion (e.g. by damaging this entity from its own explosion. /// diff --git a/Content.Server/Explosion/Components/RepeatingTriggerComponent.cs b/Content.Server/Explosion/Components/RepeatingTriggerComponent.cs new file mode 100644 index 00000000000..cc08de53f90 --- /dev/null +++ b/Content.Server/Explosion/Components/RepeatingTriggerComponent.cs @@ -0,0 +1,25 @@ +using Content.Server.Explosion.EntitySystems; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Server.Explosion.Components; + +/// +/// Constantly triggers after being added to an entity. +/// +[RegisterComponent, Access(typeof(TriggerSystem))] +[AutoGenerateComponentPause] +public sealed partial class RepeatingTriggerComponent : Component +{ + /// + /// How long to wait between triggers. + /// The first trigger starts this long after the component is added. + /// + [DataField] + public TimeSpan Delay = TimeSpan.FromSeconds(1); + + /// + /// When the next trigger will be. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField] + public TimeSpan NextTrigger; +} diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index 23543895e6f..8734c054d64 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -160,7 +160,7 @@ public void TriggerExplosive(EntityUid uid, ExplosiveComponent? explosive = null if (explosive.Exploded) return; - explosive.Exploded = true; + explosive.Exploded = !explosive.Repeatable; // Override the explosion intensity if optional arguments were provided. if (radius != null) diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index 3675c214b14..8e0a75ea24f 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -94,6 +94,7 @@ public override void Initialize() SubscribeLocalEvent(OnStepTriggered); SubscribeLocalEvent(OnSlipTriggered); SubscribeLocalEvent(OnEmptyTriggered); + SubscribeLocalEvent(OnRepeatInit); SubscribeLocalEvent(OnSpawnTrigger); SubscribeLocalEvent(HandleDeleteTrigger); @@ -241,6 +242,11 @@ private void OnEmptyTriggered(EntityUid uid, TriggerWhenEmptyComponent component Trigger(uid, args.EmptyGun); } + private void OnRepeatInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextTrigger = _timing.CurTime + ent.Comp.Delay; + } + public bool Trigger(EntityUid trigger, EntityUid? user = null) { var triggerEvent = new TriggerEvent(trigger, user); @@ -323,6 +329,7 @@ public override void Update(float frameTime) UpdateProximity(); UpdateTimer(frameTime); UpdateTimedCollide(frameTime); + UpdateRepeat(); } private void UpdateTimer(float frameTime) @@ -357,5 +364,19 @@ private void UpdateTimer(float frameTime) _appearance.SetData(uid, TriggerVisuals.VisualState, TriggerVisualState.Unprimed, appearance); } } + + private void UpdateRepeat() + { + var now = _timing.CurTime; + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.NextTrigger > now) + continue; + + comp.NextTrigger = now + comp.Delay; + Trigger(uid); + } + } } } diff --git a/Content.Shared/Weapons/Ranged/Components/ActionGunComponent.cs b/Content.Shared/Weapons/Ranged/Components/ActionGunComponent.cs new file mode 100644 index 00000000000..112339efd74 --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Components/ActionGunComponent.cs @@ -0,0 +1,37 @@ +using Content.Shared.Actions; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Weapons.Ranged.Components; + +/// +/// Lets you shoot a gun using an action. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(ActionGunSystem))] +public sealed partial class ActionGunComponent : Component +{ + /// + /// Action to create, must use . + /// + [DataField(required: true)] + public EntProtoId Action = string.Empty; + + [DataField] + public EntityUid? ActionEntity; + + /// + /// Prototype of gun entity to spawn. + /// Deleted when this component is removed. + /// + [DataField(required: true)] + public EntProtoId GunProto = string.Empty; + + [DataField] + public EntityUid? Gun; +} + +/// +/// Action event for to shoot at a position. +/// +public sealed partial class ActionGunShootEvent : WorldTargetActionEvent; diff --git a/Content.Shared/Weapons/Ranged/Systems/ActionGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/ActionGunSystem.cs new file mode 100644 index 00000000000..f3dfe8a2a03 --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Systems/ActionGunSystem.cs @@ -0,0 +1,41 @@ +using Content.Shared.Actions; +using Content.Shared.Weapons.Ranged.Components; + +namespace Content.Shared.Weapons.Ranged.Systems; + +public sealed class ActionGunSystem : EntitySystem +{ + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly SharedGunSystem _gun = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnShoot); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + if (string.IsNullOrEmpty(ent.Comp.Action)) + return; + + _actions.AddAction(ent, ref ent.Comp.ActionEntity, ent.Comp.Action); + ent.Comp.Gun = Spawn(ent.Comp.GunProto); + } + + private void OnShutdown(Entity ent, ref ComponentShutdown args) + { + if (ent.Comp.Gun is {} gun) + QueueDel(gun); + } + + private void OnShoot(Entity ent, ref ActionGunShootEvent args) + { + if (TryComp(ent.Comp.Gun, out var gun)) + _gun.AttemptShoot(ent, ent.Comp.Gun.Value, gun, args.Target); + } +} + diff --git a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index ee0db34fc2d..cb9dc1c911a 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml @@ -91,6 +91,12 @@ speedModifierThresholds: 250: 0.7 400: 0.5 + # disable taking damage from fire, since its a fire breathing dragon + - type: Flammable + damage: + types: {} + - type: Temperature + heatDamageThreshold: 800 - type: Metabolizer solutionOnBody: false updateInterval: 0.25 @@ -141,10 +147,33 @@ spawnRiftAction: ActionSpawnRift - type: GenericAntag rule: Dragon + - type: ActionGun + action: ActionDragonsBreath + gunProto: DragonsBreathGun - type: GuideHelp guides: - MinorAntagonists +- type: entity + noSpawn: true + id: DragonsBreathGun + name: dragon's lung + description: For dragon's breathing + components: + - type: RechargeBasicEntityAmmo + rechargeCooldown: 5 + rechargeSound: + path: /Audio/Animals/space_dragon_roar.ogg + - type: BasicEntityAmmoProvider + proto: ProjectileDragonsBreath + capacity: 1 + count: 1 + - type: Gun + soundGunshot: + path: /Audio/Animals/space_dragon_roar.ogg + soundEmpty: null + projectileSpeed: 5 + - type: entity parent: BaseMobDragon id: MobDragonDungeon @@ -183,6 +212,7 @@ state: icon event: !type:DragonSpawnRiftActionEvent useDelay: 1 + priority: 3 - type: entity id: ActionDevour @@ -194,3 +224,18 @@ icon: { sprite : Interface/Actions/devour.rsi, state: icon } iconOn: { sprite : Interface/Actions/devour.rsi, state: icon-on } event: !type:DevourActionEvent + priority: 1 + +- type: entity + noSpawn: true + id: ActionDragonsBreath + name: "[color=orange]Dragon's Breath[/color]" + description: Spew out flames at anyone foolish enough to attack you! + components: + - type: WorldTargetAction + # TODO: actual sprite + icon: { sprite : Objects/Weapons/Guns/Projectiles/magic.rsi, state: fireball } + event: !type:ActionGunShootEvent + priority: 2 + checkCanAccess: false + range: 0 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml index 3556d1c8f8b..d6adcd614e3 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml @@ -30,6 +30,39 @@ - type: IgniteOnCollide fireStacks: 0.35 +- type: entity + noSpawn: true + parent: BaseBulletTrigger + id: ProjectileDragonsBreath + name: dragon's breath + description: Try not to get toasted. + components: + - type: PointLight + color: "#E25822" + radius: 3.0 + energy: 5.0 + - type: Sprite + sprite: Objects/Weapons/Guns/Projectiles/magic.rsi + layers: + - state: fireball + shader: unshaded + - type: IgnitionSource + temperature: 1000 + ignited: true + - type: RepeatingTrigger + delay: 0.5 # line of fire as well as if it hits something + - type: ExplodeOnTrigger + - type: Explosive + explosionType: FireBomb + totalIntensity: 5 # low intensity, the point is to burn attackers not to break open walls, dragons can just eat them + intensitySlope: 1 + maxIntensity: 3 + canCreateVacuum: false + deleteAfterExplosion: false + repeatable: true + - type: TimedDespawn + lifetime: 5 + - type: entity id: ProjectileAnomalyFireball name: fireball From fd11738a1d07931f30690bcfdefdfc6b2253a084 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 8 May 2024 00:26:47 +0000 Subject: [PATCH 027/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 96c8b20e766..96ce6aed314 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Cojoke-dot - changes: - - message: E-sword now lights plasma fires - type: Tweak - id: 6051 - time: '2024-02-28T21:59:35.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25665 - author: lzk228 changes: - message: Engineering structures orders in cargo now supplied via flatpacks (e.g. @@ -3855,3 +3848,10 @@ id: 6550 time: '2024-05-08T00:19:55.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27373 +- author: deltanedas + changes: + - message: Dragons can now breathe fire. + type: Add + id: 6551 + time: '2024-05-08T00:25:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/26746 From fa2223ed8deda5b4b3ec6cfc8806cbd4013c997b Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Wed, 8 May 2024 04:24:54 +0200 Subject: [PATCH 028/215] Fix preferences sent to client not being sanitized (#27789) Fucking whoops In #27742 I made it so sanitization of character profiles was moved to be *after* database load. Except that means I moved it to be after the copy of all character profiles got sent to the client. Move the sending to *also* be in that second load stage, and rename it. Fixes the issue. --- Content.Server/Database/UserDbDataManager.cs | 2 +- .../Managers/IServerPreferencesManager.cs | 2 +- .../Managers/ServerPreferencesManager.cs | 27 ++++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Content.Server/Database/UserDbDataManager.cs b/Content.Server/Database/UserDbDataManager.cs index 3f6659840ad..c58c594dbad 100644 --- a/Content.Server/Database/UserDbDataManager.cs +++ b/Content.Server/Database/UserDbDataManager.cs @@ -67,7 +67,7 @@ await Task.WhenAll( _playTimeTracking.LoadData(session, cancel)); cancel.ThrowIfCancellationRequested(); - _prefs.SanitizeData(session); + _prefs.FinishLoad(session); _sawmill.Verbose($"Load complete for user {session}"); } diff --git a/Content.Server/Preferences/Managers/IServerPreferencesManager.cs b/Content.Server/Preferences/Managers/IServerPreferencesManager.cs index 92e7adf22ad..63c267f154a 100644 --- a/Content.Server/Preferences/Managers/IServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/IServerPreferencesManager.cs @@ -12,7 +12,7 @@ public interface IServerPreferencesManager void Init(); Task LoadData(ICommonSession session, CancellationToken cancel); - void SanitizeData(ICommonSession session); + void FinishLoad(ICommonSession session); void OnClientDisconnected(ICommonSession session); bool TryGetCachedPreferences(NetUserId userId, [NotNullWhen(true)] out PlayerPreferences? playerPreferences); diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index 55532f1ff5b..7a80757435e 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -199,27 +199,28 @@ async Task LoadPrefs() { var prefs = await GetOrCreatePreferencesAsync(session.UserId, cancel); prefsData.Prefs = prefs; - prefsData.PrefsLoaded = true; - - var msg = new MsgPreferencesAndSettings(); - msg.Preferences = prefs; - msg.Settings = new GameSettings - { - MaxCharacterSlots = MaxCharacterSlots - }; - _netManager.ServerSendMessage(msg, session.Channel); } } } - public void SanitizeData(ICommonSession session) + public void FinishLoad(ICommonSession session) { // This is a separate step from the actual database load. // Sanitizing preferences requires play time info due to loadouts. // And play time info is loaded concurrently from the DB with preferences. - var data = _cachedPlayerPrefs[session.UserId]; - DebugTools.Assert(data.Prefs != null); - data.Prefs = SanitizePreferences(session, data.Prefs, _dependencies); + var prefsData = _cachedPlayerPrefs[session.UserId]; + DebugTools.Assert(prefsData.Prefs != null); + prefsData.Prefs = SanitizePreferences(session, prefsData.Prefs, _dependencies); + + prefsData.PrefsLoaded = true; + + var msg = new MsgPreferencesAndSettings(); + msg.Preferences = prefsData.Prefs; + msg.Settings = new GameSettings + { + MaxCharacterSlots = MaxCharacterSlots + }; + _netManager.ServerSendMessage(msg, session.Channel); } public void OnClientDisconnected(ICommonSession session) From 42e5ecf35b5c7ef57596bb4a1d1053666b69b819 Mon Sep 17 00:00:00 2001 From: Flareguy <78941145+Flareguy@users.noreply.github.com> Date: Tue, 7 May 2024 22:48:42 -0500 Subject: [PATCH 029/215] Revert "Make the floppy lizard ears have two colors." (#27790) Revert "Make the floppy lizard ears have two colors. (#27679)" This reverts commit bd06aa2365d6ce093ac47462deb69858c6cc18c0. --- Resources/Locale/en-US/markings/reptilian.ftl | 3 +-- .../Mobs/Customization/Markings/reptilian.yml | 4 +--- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 1 + ...imary.png => horns_floppy_kobold_ears.png} | Bin 1696 -> 1717 bytes .../horns_floppy_kobold_ears_secondary.png | Bin 197 -> 0 bytes .../reptilian_parts.rsi/meta.json | 6 +----- 6 files changed, 4 insertions(+), 10 deletions(-) rename Resources/Textures/Mobs/Customization/reptilian_parts.rsi/{horns_floppy_kobold_ears_primary.png => horns_floppy_kobold_ears.png} (74%) delete mode 100644 Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears_secondary.png diff --git a/Resources/Locale/en-US/markings/reptilian.ftl b/Resources/Locale/en-US/markings/reptilian.ftl index c2a2df4b3ba..470af07361d 100644 --- a/Resources/Locale/en-US/markings/reptilian.ftl +++ b/Resources/Locale/en-US/markings/reptilian.ftl @@ -96,8 +96,7 @@ marking-LizardHornsBighorn = Lizard Horns (Bighorn) marking-LizardHornsKoboldEars-horns_kobold_ears = Lizard Ears (Kobold) marking-LizardHornsKoboldEars = Lizard Ears (Kobold) -marking-LizardHornsFloppyKoboldEars-horns_floppy_kobold_ears_primary = Lizard Ears (Floppy Kobold) -marking-LizardHornsFloppyKoboldEars-horns-floppy_kobold_ears_secondary = Lizard Ears (Floppy Kobold) +marking-LizardHornsFloppyKoboldEars-horns_floppy_kobold_ears = Lizard Ears (Floppy Kobold) marking-LizardHornsFloppyKoboldEars = Lizard Ears (Floppy Kobold) marking-LizardChestUnderbelly-body_underbelly = Lizard Chest (Underbelly) diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml index 95cfb72e875..5a52e09bf2d 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml @@ -310,9 +310,7 @@ speciesRestriction: [Reptilian] sprites: - sprite: Mobs/Customization/reptilian_parts.rsi - state: horns_floppy_kobold_ears_primary - - sprite: Mobs/Customization/reptilian_parts.rsi - state: horns_floppy_kobold_ears_secondary + state: horns_floppy_kobold_ears - type: marking id: LizardChestUnderbelly diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 6bc0d7a598f..7fb431fabb5 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1434,6 +1434,7 @@ horns_bighorn: KoboldHornColors horns_argali: KoboldHornColors horns_ayrshire: KoboldHornColors + horns_floppy_kobold_ears: Inherit horns_double: Inherit horns_kobold_ears: Inherit - type: Butcherable diff --git a/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears_primary.png b/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears.png similarity index 74% rename from Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears_primary.png rename to Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears.png index 89d7eaef817d3abd4702b58e71d92c1ffdda29b4..061c0100cc5cc0d2216f0e25819a546421c7d300 100644 GIT binary patch delta 343 zcmV-d0jU0<4Yduh-2pxiP)t-sdU|@dx3{FEq?DAD0000ux)%Ka000GaQchF<|Ns90 z`?d~K00009a7bBm001bN001bN0lI?#-?JA2kpqA9Nkl zFrv3a!%{BlYApt#?Mr)@E(BbJ;sIKlvI&s=(hFjD5M)i8U|9%IYhHo3Mh`sz<~!uK zXaIkpg~bG@=`sM=fodOgwGPheHNaB<0w4-N07L=U0ZuM2^5dP07QYW#vUvqaeyY9E z60IlTksvd70JS&r0qp{KW@?|>07s}E+P*IU!24&a8{%w7!f@QrZ~g{6*+#X%8@yW= p;pponPXP#kC;$Nv{Tfi~2lKN%Lkjt^{s0RA002ovPDHLkV1hgVk!An@ delta 321 zcmV-H0lxmV4WJFM-2plbP)t-sdU|@dx3{FEqyPW_*0rV`00004bW%=J|Ns90Kq{&9 z00009a7bBm001bK001bK0nAOV#j_g%kpq9@Nkl{xl= z5rPmfAqW8z0xNL#vcg~Ql*syhK<#EN;QiUd8`NrD1&;>qxfAFOw?Qv=C6Ei?*%sSn z1j|xgF#i`Ipm|*3F0|XxFe0j#lh1%UrL`w`gR^l4TaSl4LJ$Hb1R-F4EvWSapJ^^9 Tc?DQO00000NkvXXu0mjfcEE>} diff --git a/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears_secondary.png b/Resources/Textures/Mobs/Customization/reptilian_parts.rsi/horns_floppy_kobold_ears_secondary.png deleted file mode 100644 index b40d099eabd5c9969e85ab0a30ac36185fdccf56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sEXgD|57Yp@DXFv`=#F{C2y?IlCL1_KV(fG=}w?cu7{dLP*Y(}< Date: Tue, 7 May 2024 23:23:26 -0700 Subject: [PATCH 030/215] Fix the changelog window being very laggy until a tab is clicked (#27795) --- Content.Client/Changelog/ChangelogWindow.xaml.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Content.Client/Changelog/ChangelogWindow.xaml.cs b/Content.Client/Changelog/ChangelogWindow.xaml.cs index e5f492900c2..9b7fd754369 100644 --- a/Content.Client/Changelog/ChangelogWindow.xaml.cs +++ b/Content.Client/Changelog/ChangelogWindow.xaml.cs @@ -87,14 +87,12 @@ private void TabsUpdated() if (!tab.AdminOnly || isAdmin) { Tabs.SetTabVisible(i, true); - tab.Visible = true; visibleTabs++; firstVisible ??= i; } else { Tabs.SetTabVisible(i, false); - tab.Visible = false; } } From d442be160d8a09579f265cb85382f058719ecda6 Mon Sep 17 00:00:00 2001 From: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Date: Wed, 8 May 2024 01:46:03 -0500 Subject: [PATCH 031/215] Shoot Over Racks (#27797) Racks now have table collisions --- Resources/Prototypes/Entities/Structures/Storage/storage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Storage/storage.yml b/Resources/Prototypes/Entities/Structures/Storage/storage.yml index 79a1e357998..d0b468ca53d 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/storage.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/storage.yml @@ -27,9 +27,9 @@ bounds: "-0.3,-0.3,0.3,0.3" density: 140 mask: - - MachineMask + - TableMask layer: - - MachineLayer + - TableLayer - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic From d6406eafef90852c90116d0ebc5f88154bf6e4ec Mon Sep 17 00:00:00 2001 From: Verm <32827189+Vermidia@users.noreply.github.com> Date: Wed, 8 May 2024 01:46:48 -0500 Subject: [PATCH 032/215] Fix cak and breaddog not being able to escape inventories (#27794) Fix cak and breaddog --- .../Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml | 1 + .../Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml index 71b3066a265..e40ac40a289 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/bread.yml @@ -829,6 +829,7 @@ - type: Puller needsHands: false - type: Examiner + - type: DoAfter - type: CombatMode - type: MeleeWeapon soundHit: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml index cbb9ff2c45b..c939dec52c6 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/cake.yml @@ -691,6 +691,7 @@ - type: Puller needsHands: false - type: Examiner + - type: DoAfter - type: CombatMode - type: MeleeWeapon soundHit: From 0e8c8110d1af646e38d7e94bfe6d127bddd18b86 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 8 May 2024 06:47:10 +0000 Subject: [PATCH 033/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 96ce6aed314..bc1f2b6dd65 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: lzk228 - changes: - - message: Engineering structures orders in cargo now supplied via flatpacks (e.g. - tesla & singulo generators, other stuff for them). - type: Tweak - - message: Thrusters and gyroscopes now supplied in crates via flatpacks. - type: Tweak - id: 6052 - time: '2024-02-28T22:02:02.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25647 - author: c4llv07e changes: - message: Cryo won't remove you from the new body if you chose a new ghost role @@ -3855,3 +3845,10 @@ id: 6551 time: '2024-05-08T00:25:41.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/26746 +- author: Cojoke-dot + changes: + - message: You can now shoot over racks + type: Tweak + id: 6552 + time: '2024-05-08T06:46:04.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27797 From bf559f6e275bde59c5f5d7cb14dc452b8cd19497 Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Tue, 7 May 2024 23:49:28 -0700 Subject: [PATCH 034/215] Fix pull not stopping when character is downed (#27796) --- .../Movement/Pulling/Systems/PullingSystem.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index 3c265d5a027..81b2fee5695 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -13,6 +13,7 @@ using Content.Shared.Movement.Pulling.Events; using Content.Shared.Movement.Systems; using Content.Shared.Pulling.Events; +using Content.Shared.Standing; using Content.Shared.Throwing; using Content.Shared.Verbs; using Robust.Shared.Containers; @@ -62,6 +63,7 @@ public override void Initialize() SubscribeLocalEvent(OnPullerUnpaused); SubscribeLocalEvent(OnVirtualItemDeleted); SubscribeLocalEvent(OnRefreshMovespeed); + SubscribeLocalEvent(OnDropHandItems); CommandBinds.Builder .Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(OnRequestMovePulledObject)) @@ -69,6 +71,17 @@ public override void Initialize() .Register(); } + private void OnDropHandItems(EntityUid uid, PullerComponent pullerComp, DropHandItemsEvent args) + { + if (pullerComp.Pulling == null || pullerComp.NeedsHands) + return; + + if (!TryComp(pullerComp.Pulling, out PullableComponent? pullableComp)) + return; + + TryStopPull(pullerComp.Pulling.Value, pullableComp, uid); + } + private void OnPullerContainerInsert(Entity ent, ref EntGotInsertedIntoContainerMessage args) { if (ent.Comp.Pulling == null) return; From 49e26b17829f195928d513d7b8addc7dd9e314ef Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 8 May 2024 06:50:34 +0000 Subject: [PATCH 035/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index bc1f2b6dd65..e078db59e76 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: c4llv07e - changes: - - message: Cryo won't remove you from the new body if you chose a new ghost role - before cryo deleted your old body. - type: Fix - id: 6053 - time: '2024-02-28T22:09:02.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/24991 - author: Nairodian changes: - message: You can now remove the rust from reinforced walls with a welder. @@ -3852,3 +3844,10 @@ id: 6552 time: '2024-05-08T06:46:04.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27797 +- author: ShadowCommander + changes: + - message: Lizards now stop tail pulling when they are downed. + type: Fix + id: 6553 + time: '2024-05-08T06:49:28.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27796 From 540aeb289f69d35b35b64d644170fd2cef528b20 Mon Sep 17 00:00:00 2001 From: Kukutis96513 <146854220+Kukutis96513@users.noreply.github.com> Date: Wed, 8 May 2024 09:57:21 +0300 Subject: [PATCH 036/215] Secoff senior uniform fix (#27775) * weh. * get the weh out of my repository * add best lizard figurine * remove umbra stuff from master * remove the additional pixels from the senior secoff jumpskirt sprite * fix the fix --- .../equipped-INNERCLOTHING.png | Bin 1316 -> 1245 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/senior_officer.rsi/equipped-INNERCLOTHING.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/senior_officer.rsi/equipped-INNERCLOTHING.png index b3ecbd97129997ad4c081960cc16e9231023ace4..7dce3cbc9b1d6851e24e3f95c75047d1e19824f9 100644 GIT binary patch delta 1205 zcmV;m1WNm)3f&2iBpLy8Qb$4nuFf3k00004XF*Lt006O%3;baPks%m=a!Eu%RCr$P zn$JsAQ546Izsk`8Ar%o7GzEolA`){U;i80yf*`P+yB0w~iyb4V zk%XWLk&OfiPD&VqxEdLMl*%;U+nnk8oMzs6zV{5e9~kbB_vU`jIrqLd=s9FC7z_r3 z!C(*&4u?~juEIL@n%>@j-jwx|xg@42)7oOFxlIXu>}aFT&Q6L%B1Le1VPS#JeOXfX zjI1n))|3GK*79|kAsd{ysfxZlq)~Nx@){Zmz6SLrICc z_HJ|2lOS02;q2E7=jrtO56XF``o)bsC*Sa|VR=(Z4qguAR5P6>!jy{}=_w~~H>nSxKDlJv5 z{u1Ko16=1LdzANofv(T%V{JYWI($#r~t zx|$Lj8&q6eO!M>es>K-dxb7uZ{k65VBkPL-0GgVbsJ6D2y1Kd!w3zn;xAkoYpuN34 z1);jSIyFZ0dwP0kc6L^^%gf8u-QBJ3Q#ZH_*2VhJ(RE=5fL*XpsxLYLEiEkv9AHp5Agr&iD-lE+(MLNs zH>c+K+Vu3a>%Ro}lb6>=%Z~%wp=bUjAgn~#I@5J#n$YINEc#9m1z=`ohTdGcmMuQu3@pTBu~>HOCCb^f z7CWqKI@t(O08k*o!szEpi~9+*VFTmj6o>-A4zts2K(2}nud&u8uw8mU>>zq$BPnsB ztSmG7Lm&{y)(eb}k9+znFfcHX;+ll*^vBA7&)r>7f<*i)P@R4QLqu=&dI7J|YZ7wV zB1Ig4a7e)Cd?127|#aM85#$L_`}9 Tg<@cy00000NkvXXu0mjfQIt&a delta 1278 zcmVYXc~GI%D|?*eQN^fNd(NGg>IB$G*ZdwZJ?Uc3ZgHv3X(PF4NBa2U&RkT^tF&a3Lz zJa{J7@7`@~1z@jK;+I%AhlhtmA`w2x?$Tu#0IU`YO-VwfzU4T)8of>L;|==4;kxyM zD&nh!0svixakA*Hf38!DzuVgCRZ63%)ED;m_sgEwqkmxn=H}+y2alH6E0stWi|mz3 zz)2E1Iy$_?%j-ZxlOSaec>0#(&=(HV*4E~gzF+^rqb26%=4#TvFag=w6ab|!KL_yc zze?nM>n}b7{*C)0&b?Rsn@(4lkO2IPm2r}kh23l*T`aO%D4ZIn zieG4$0wqVl<1g-uRAe(+tPv))%PRp)gac)Rn$MOSyt1&2UO|{m(j23V^9c%LfmR^XaC@U zU@*wm))v8Fko|)LZnd`)3SC%Gm!eYNw(U#nD6(gG#MTqE<5)0^eBthL!QGd4CxCX-=gWQ0T_f6-!Ro3b3cwm zyspfRufMwTMEDo}*SFt!vHW&;sGJ0)!BgEae^(lx(vp(LOYgr&@8gZ8Iz-^4Vft$H zHus)Br^_%{EfhS1g!BVdlAzoeiOnP#_~Uo4AGcR3(PbFid-|M%srwB#NBEhE^}7dC z_r1l7CqVoG;vi8$DFGinoMCculEuYEaueeWg+iWqWtX;@_?x2Atv<`Qa1lz)w6(Q0 ze~j;cAnQ0VG46GWEyuwEEX%@ZP*=Db>0*(RWtEvF{9Ryiuc)9@0r}+>FVp$ul`^lu z=0HEjvln&k#I>@pn++JxUa&dP50v{7;B5EGc_>vtr8%O~D{z%m`V#yOk_Cl%loIgk z4TE;M1&07*qoM6N<$f=N4s-v9sr From cf43305d9ee4262c7d6e14f9c83e12446cdc9e60 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Wed, 8 May 2024 00:30:03 -0700 Subject: [PATCH 037/215] Reimplement supplybots as non-vehicles (#27769) * Reimplement supplybots as non-vehicles * what the hell is a container container? * Dumpable * let them hear supply comms * unmigrate * no more QM access * Skill issue --------- Co-authored-by: plykiya --- Resources/Migrations/migration.yml | 2 +- .../Prototypes/Entities/Mobs/NPCs/silicon.yml | 54 +++++++++++++++++++ .../Crafting/Graphs/bots/supplybot.yml | 23 ++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 Resources/Prototypes/Recipes/Crafting/Graphs/bots/supplybot.yml diff --git a/Resources/Migrations/migration.yml b/Resources/Migrations/migration.yml index 99b24d98c22..974fca5effb 100644 --- a/Resources/Migrations/migration.yml +++ b/Resources/Migrations/migration.yml @@ -188,7 +188,7 @@ ActionVehicleHorn: null CrateFunATV: null CrateFunSyndicateSegway: null MobTaxiBot: null -MobSupplyBot: null +# MobSupplyBot: null SpawnVehicleMotobike: null SpawnVehicleATV: null SpawnVehicleSecway: null diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index 2423082fd8d..b405a67d069 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -327,3 +327,57 @@ interactFailureString: petting-failure-mimebot - type: Inventory templateId: head + +- type: entity + parent: MobSiliconBase + id: MobSupplyBot + name: supplybot + description: Delivers cargo! + components: + - type: Sprite + sprite: Mobs/Silicon/Bots/supplybot.rsi + layers: + - state: supplybot + - type: GhostRole + name: ghost-role-information-supplybot-name + description: ghost-role-information-supplybot-description + - type: Construction + graph: SupplyBot + node: bot + - type: Access + tags: + - Cargo + - Maintenance + - Salvage + - type: Dumpable + - type: Storage + maxItemSize: Huge + grid: + - 0,0,9,3 + - type: UserInterface + interfaces: + enum.StorageUiKey.Key: + type: StorageBoundUserInterface + - type: ContainerContainer + containers: + storagebase: !type:Container + ents: [] + - type: UnpoweredFlashlight + - type: PointLight + enabled: false + radius: 3.5 + softness: 2 + mask: /Textures/Effects/LightMasks/cone.png + autoRot: true + - type: FootstepModifier + footstepSoundCollection: + collection: FootstepBorg + - type: Tag + tags: + - DoorBumpOpener + - FootstepSound + - type: ActiveRadio + channels: + - Binary + - Common + - Supply diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/supplybot.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/supplybot.yml new file mode 100644 index 00000000000..efabb849bbc --- /dev/null +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/supplybot.yml @@ -0,0 +1,23 @@ +- type: constructionGraph + id: SupplyBot + start: start + graph: + - node: start + edges: + - to: bot + steps: + - tag: ProximitySensor + icon: + sprite: Objects/Misc/proximity_sensor.rsi + state: icon + name: proximity sensor + - tag: BorgHead + icon: + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + state: borg_head + name: borg head + doAfter: 1 + - material: Steel + amount: 10 + - node: bot + entity: MobSupplyBot From 5007684cecb361bb8db483c887599f4d3f853528 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 8 May 2024 07:31:59 +0000 Subject: [PATCH 038/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index e078db59e76..87acc64faf7 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,18 +1,4 @@ Entries: -- author: Nairodian - changes: - - message: You can now remove the rust from reinforced walls with a welder. - type: Fix - id: 6054 - time: '2024-02-29T00:44:01.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25690 -- author: Ubaser - changes: - - message: Eris UI theme has been removed. - type: Remove - id: 6055 - time: '2024-02-29T05:53:48.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25673 - author: Rainfey changes: - message: Disallow multiple antag roles per player @@ -3851,3 +3837,17 @@ id: 6553 time: '2024-05-08T06:49:28.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27796 +- author: Plykiya + changes: + - message: Re-added supplybots. + type: Add + id: 6554 + time: '2024-05-08T07:30:04.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27769 +- author: Hobbitmax + changes: + - message: Replaced Syndicate Jaws Of Life with a Pyjama bundle on the Train map. + type: Tweak + id: 6555 + time: '2024-05-08T07:30:53.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27734 From 094ea59b956c6649615efcb4c571108284af223b Mon Sep 17 00:00:00 2001 From: Tornado Tech <54727692+Tornado-Technology@users.noreply.github.com> Date: Wed, 8 May 2024 19:18:03 +1000 Subject: [PATCH 039/215] Added new HTN operations and preconditions (#27486) * Added new HTN operations & preconditions * Ok I forgot about partial * Namespace pierce the skies * Some fixes, debug and new operators * Bruh git eat my files --- .../HTN/Preconditions/GunAmmoPrecondition.cs | 2 +- .../Preconditions/KeyNotExistsPrecondition.cs | 12 +++ .../Math/KeyBoolEqualsPrecondition.cs | 23 +++++ .../Math/KeyFloatEqualsPrecondition.cs | 18 ++++ .../Math/KeyFloatGreaterPrecondition.cs | 17 ++++ .../Math/KeyFloatLessPrecondition.cs | 17 ++++ .../Operators/Math/AddFloatOperator.cs | 33 +++++++ .../Operators/Math/SetBoolOperator.cs | 28 ++++++ .../Operators/{ => Math}/SetFloatOperator.cs | 18 ++-- .../Operators/Math/SetRandomFloatOperator.cs | 34 +++++++ .../Operators/PlaySoundOperator.cs | 28 ++++++ .../Operators/SayKeyOperator.cs | 36 ++++++++ .../Entities/Mobs/Debugging/debug_counter.yml | 53 +++++++++++ Resources/Prototypes/NPCs/debug.yml | 90 +++++++++++++++++++ 14 files changed, 401 insertions(+), 8 deletions(-) create mode 100644 Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs create mode 100644 Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs create mode 100644 Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs create mode 100644 Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs create mode 100644 Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs rename Content.Server/NPC/HTN/PrimitiveTasks/Operators/{ => Math}/SetFloatOperator.cs (52%) create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/PlaySoundOperator.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs create mode 100644 Resources/Prototypes/Entities/Mobs/Debugging/debug_counter.yml create mode 100644 Resources/Prototypes/NPCs/debug.yml diff --git a/Content.Server/NPC/HTN/Preconditions/GunAmmoPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/GunAmmoPrecondition.cs index fe3b844ae34..58647d88749 100644 --- a/Content.Server/NPC/HTN/Preconditions/GunAmmoPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/GunAmmoPrecondition.cs @@ -35,7 +35,7 @@ public override bool IsMet(NPCBlackboard blackboard) else percent = ammoEv.Count / (float) ammoEv.Capacity; - percent = Math.Clamp(percent, 0f, 1f); + percent = System.Math.Clamp(percent, 0f, 1f); if (MaxPercent < percent) return false; diff --git a/Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs new file mode 100644 index 00000000000..c12663901c7 --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/KeyNotExistsPrecondition.cs @@ -0,0 +1,12 @@ +namespace Content.Server.NPC.HTN.Preconditions; + +public sealed partial class KeyNotExistsPrecondition : HTNPrecondition +{ + [DataField(required: true)] + public string Key = string.Empty; + + public override bool IsMet(NPCBlackboard blackboard) + { + return !blackboard.ContainsKey(Key); + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs new file mode 100644 index 00000000000..8c7920e8be5 --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyBoolEqualsPrecondition.cs @@ -0,0 +1,23 @@ +namespace Content.Server.NPC.HTN.Preconditions.Math; + +/// +/// Checks for the presence of data in the blackboard and makes a comparison with the specified boolean +/// +public sealed partial class KeyBoolEqualsPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + [DataField(required: true)] + public string Key = string.Empty; + + [DataField(required: true)] + public bool Value; + + public override bool IsMet(NPCBlackboard blackboard) + { + if (!blackboard.TryGetValue(Key, out var value, _entManager)) + return false; + + return Value == value; + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs new file mode 100644 index 00000000000..802fdaf2b9c --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatEqualsPrecondition.cs @@ -0,0 +1,18 @@ +namespace Content.Server.NPC.HTN.Preconditions.Math; + +public sealed partial class KeyFloatEqualsPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + [DataField(required: true)] + public string Key = string.Empty; + + [DataField(required: true)] + public float Value; + + public override bool IsMet(NPCBlackboard blackboard) + { + return blackboard.TryGetValue(Key, out var value, _entManager) && + MathHelper.CloseTo(value, value); + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs new file mode 100644 index 00000000000..3a9ac366980 --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatGreaterPrecondition.cs @@ -0,0 +1,17 @@ +namespace Content.Server.NPC.HTN.Preconditions.Math; + +public sealed partial class KeyFloatGreaterPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + [DataField(required: true)] + public string Key = string.Empty; + + [DataField(required: true)] + public float Value; + + public override bool IsMet(NPCBlackboard blackboard) + { + return blackboard.TryGetValue(Key, out var value, _entManager) && value > Value; + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs new file mode 100644 index 00000000000..5cd51d7a7c5 --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/Math/KeyFloatLessPrecondition.cs @@ -0,0 +1,17 @@ +namespace Content.Server.NPC.HTN.Preconditions.Math; + +public sealed partial class KeyFloatLessPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + [DataField(required: true)] + public string Key = string.Empty; + + [DataField(required: true)] + public float Value; + + public override bool IsMet(NPCBlackboard blackboard) + { + return blackboard.TryGetValue(Key, out var value, _entManager) && value < Value; + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs new file mode 100644 index 00000000000..00404517c9e --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/AddFloatOperator.cs @@ -0,0 +1,33 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; + +/// +/// Gets the key, and adds the value to that float +/// +public sealed partial class AddFloatOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + [DataField(required: true)] + public string TargetKey = string.Empty; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float Amount; + + public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, + CancellationToken cancelToken) + { + if (!blackboard.TryGetValue(TargetKey, out var value, _entManager)) + return (false, null); + + return ( + true, + new Dictionary + { + { TargetKey, value + Amount } + } + ); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs new file mode 100644 index 00000000000..a40b96798d4 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetBoolOperator.cs @@ -0,0 +1,28 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; + +/// +/// Just sets a blackboard key to a bool +/// +public sealed partial class SetBoolOperator : HTNOperator +{ + [DataField(required: true)] + public string TargetKey = string.Empty; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool Value; + + public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, + CancellationToken cancelToken) + { + return ( + true, + new Dictionary + { + { TargetKey, Value } + } + ); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SetFloatOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetFloatOperator.cs similarity index 52% rename from Content.Server/NPC/HTN/PrimitiveTasks/Operators/SetFloatOperator.cs rename to Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetFloatOperator.cs index 7a460592cb4..76842b431f7 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SetFloatOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetFloatOperator.cs @@ -1,24 +1,28 @@ using System.Threading; using System.Threading.Tasks; -namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators; +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; /// /// Just sets a blackboard key to a float /// public sealed partial class SetFloatOperator : HTNOperator { - [DataField("targetKey", required: true)] public string TargetKey = string.Empty; + [DataField(required: true)] + public string TargetKey = string.Empty; - [ViewVariables(VVAccess.ReadWrite), DataField("amount")] + [DataField, ViewVariables(VVAccess.ReadWrite)] public float Amount; public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, CancellationToken cancelToken) { - return (true, new Dictionary() - { - {TargetKey, Amount}, - }); + return ( + true, + new Dictionary + { + { TargetKey, Amount } + } + ); } } diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs new file mode 100644 index 00000000000..999756f1f74 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Math/SetRandomFloatOperator.cs @@ -0,0 +1,34 @@ +using System.Threading; +using System.Threading.Tasks; +using Robust.Shared.Random; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Math; + +/// +/// Sets a random float from MinAmount to MaxAmount to blackboard +/// +public sealed partial class SetRandomFloatOperator : HTNOperator +{ + [Dependency] private readonly IRobustRandom _random = default!; + + [DataField(required: true)] + public string TargetKey = string.Empty; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float MaxAmount = 1f; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float MinAmount = 0f; + + public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, + CancellationToken cancelToken) + { + return ( + true, + new Dictionary + { + { TargetKey, _random.NextFloat(MinAmount, MaxAmount) } + } + ); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PlaySoundOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PlaySoundOperator.cs new file mode 100644 index 00000000000..57cc2e91e44 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/PlaySoundOperator.cs @@ -0,0 +1,28 @@ +using Robust.Server.Audio; +using Robust.Shared.Audio; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators; + +public sealed partial class PlaySoundOperator : HTNOperator +{ + private AudioSystem _audio = default!; + + [DataField(required: true)] + public SoundSpecifier? Sound; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + + _audio = IoCManager.Resolve().GetEntitySystem(); + } + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + var uid = blackboard.GetValue(NPCBlackboard.Owner); + + _audio.PlayPvs(Sound, uid); + + return base.Update(blackboard, frameTime); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs new file mode 100644 index 00000000000..d1c7d619150 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/SayKeyOperator.cs @@ -0,0 +1,36 @@ +using Content.Server.Chat.Systems; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators; + +public sealed partial class SayKeyOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + private ChatSystem _chat = default!; + + [DataField(required: true)] + public string Key = string.Empty; + + /// + /// Whether to hide message from chat window and logs. + /// + [DataField] + public bool Hidden; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _chat = IoCManager.Resolve().GetEntitySystem(); + } + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + if (!blackboard.TryGetValue(Key, out var value, _entManager)) + return HTNOperatorStatus.Failed; + + var speaker = blackboard.GetValue(NPCBlackboard.Owner); + _chat.TrySendInGameICMessage(speaker, value.ToString() ?? "Oh no...", InGameICChatType.Speak, hideChat: Hidden, hideLog: Hidden); + + return base.Update(blackboard, frameTime); + } +} diff --git a/Resources/Prototypes/Entities/Mobs/Debugging/debug_counter.yml b/Resources/Prototypes/Entities/Mobs/Debugging/debug_counter.yml new file mode 100644 index 00000000000..05707c7151c --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Debugging/debug_counter.yml @@ -0,0 +1,53 @@ +- type: entity + parent: MobHuman + id: MobDebugCounter + name: debug counter + description: He can count + suffix: AI, DEBUG + components: + - type: HTN + rootTask: + task: DebugCounterCompound + blackboard: + MinimumIdleTime: !type:Single + 0.5 + MaximumIdleTime: !type:Single + 0.5 + Count: !type:Single + 0 + +- type: entity + parent: MobHuman + id: MobDebugRandomCounter + name: debug random counter + description: He can randomize + suffix: AI, DEBUG + components: + - type: HTN + rootTask: + task: DebugRandomCounterCompound + blackboard: + MinimumIdleTime: !type:Single + 1 + MaximumIdleTime: !type:Single + 1 + Count: !type:Single + 0 + +- type: entity + parent: MobHuman + id: MobDebugRandomLess + name: debug random less + description: He can lessing + suffix: AI, DEBUG + components: + - type: HTN + rootTask: + task: DebugRandomLessCompound + blackboard: + MinimumIdleTime: !type:Single + 1 + MaximumIdleTime: !type:Single + 1 + Count: !type:Single + 0 diff --git a/Resources/Prototypes/NPCs/debug.yml b/Resources/Prototypes/NPCs/debug.yml new file mode 100644 index 00000000000..c7929be1037 --- /dev/null +++ b/Resources/Prototypes/NPCs/debug.yml @@ -0,0 +1,90 @@ +- type: htnCompound + id: DebugCounterCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:AddFloatOperator + targetKey: Count + amount: 1 + + - !type:HTNPrimitiveTask + operator: !type:SayKeyOperator + key: Count + + - !type:HTNPrimitiveTask + operator: !type:RandomOperator + targetKey: IdleTime + minKey: MinimumIdleTime + maxKey: MaximumIdleTime + + - !type:HTNPrimitiveTask + operator: !type:WaitOperator + key: IdleTime + preconditions: + - !type:KeyExistsPrecondition + key: IdleTime + +- type: htnCompound + id: DebugRandomCounterCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:SetRandomFloatOperator + targetKey: Count + minAmount: 0 + maxAmount: 100 + + - !type:HTNPrimitiveTask + operator: !type:SayKeyOperator + key: Count + + - !type:HTNPrimitiveTask + operator: !type:RandomOperator + targetKey: IdleTime + minKey: MinimumIdleTime + maxKey: MaximumIdleTime + + - !type:HTNPrimitiveTask + operator: !type:WaitOperator + key: IdleTime + preconditions: + - !type:KeyExistsPrecondition + key: IdleTime + +- type: htnCompound + id: DebugRandomLessCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:SetRandomFloatOperator + targetKey: Count + minAmount: 0 + maxAmount: 100 + + - !type:HTNPrimitiveTask + operator: !type:SayKeyOperator + key: Count + preconditions: + - !type:KeyFloatLessPrecondition + key: Count + value: 50 + + - !type:HTNPrimitiveTask + operator: !type:RandomOperator + targetKey: IdleTime + minKey: MinimumIdleTime + maxKey: MaximumIdleTime + + - !type:HTNPrimitiveTask + operator: !type:WaitOperator + key: IdleTime + preconditions: + - !type:KeyExistsPrecondition + key: IdleTime + + - tasks: + - !type:HTNPrimitiveTask + operator: !type:SpeakOperator + speech: "fuck!" + + \ No newline at end of file From 25de5110941ab37eca628df99154fa2fdce878d9 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Wed, 8 May 2024 03:21:59 -0700 Subject: [PATCH 040/215] Katana Dash checks based on vision rather than collision (#27793) * Fix dash check * unused * obsolete function --------- Co-authored-by: plykiya --- Content.Shared/Ninja/Systems/DashAbilitySystem.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Content.Shared/Ninja/Systems/DashAbilitySystem.cs b/Content.Shared/Ninja/Systems/DashAbilitySystem.cs index f9e5d4a1f63..4853968b61f 100644 --- a/Content.Shared/Ninja/Systems/DashAbilitySystem.cs +++ b/Content.Shared/Ninja/Systems/DashAbilitySystem.cs @@ -4,8 +4,8 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Ninja.Components; -using Content.Shared.Physics; using Content.Shared.Popups; +using Content.Shared.Examine; using Robust.Shared.Audio.Systems; using Robust.Shared.Timing; @@ -20,7 +20,7 @@ public sealed class DashAbilitySystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedChargesSystem _charges = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; - [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; @@ -79,11 +79,10 @@ private void OnDash(EntityUid uid, DashAbilityComponent comp, DashEvent args) _popup.PopupClient(Loc.GetString("dash-ability-no-charges", ("item", uid)), user, user); return; } - - var origin = Transform(user).MapPosition; + var origin = _transform.GetMapCoordinates(user); var target = args.Target.ToMap(EntityManager, _transform); // prevent collision with the user duh - if (!_interaction.InRangeUnobstructed(origin, target, 0f, CollisionGroup.Opaque, uid => uid == user)) + if (!_examine.InRangeUnOccluded(origin, target, SharedInteractionSystem.MaxRaycastRange, null)) { // can only dash if the destination is visible on screen _popup.PopupClient(Loc.GetString("dash-ability-cant-see", ("item", uid)), user, user); From 96b145c5e96dc85a1b42e52f00fb2d02ef57a2c8 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Wed, 8 May 2024 03:22:09 -0700 Subject: [PATCH 041/215] Move step sound distance and footstep variation to MobMoverComponent (#27799) --- .../Movement/Components/MobMoverComponent.cs | 9 +++++++++ .../Movement/Systems/SharedMoverController.cs | 11 ++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Content.Shared/Movement/Components/MobMoverComponent.cs b/Content.Shared/Movement/Components/MobMoverComponent.cs index a77f415b938..7ad7961ed7e 100644 --- a/Content.Shared/Movement/Components/MobMoverComponent.cs +++ b/Content.Shared/Movement/Components/MobMoverComponent.cs @@ -14,6 +14,15 @@ public sealed partial class MobMoverComponent : Component [DataField] public float PushStrength = 600f; + [DataField, AutoNetworkedField] + public float StepSoundMoveDistanceRunning = 2; + + [DataField, AutoNetworkedField] + public float StepSoundMoveDistanceWalking = 1.5f; + + [DataField, AutoNetworkedField] + public float FootstepVariation; + [ViewVariables(VVAccess.ReadWrite)] public EntityCoordinates LastPosition { get; set; } diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index 5eaa1a78ee3..0e388ab853b 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -60,11 +60,6 @@ public abstract partial class SharedMoverController : VirtualController protected EntityQuery CanMoveInAirQuery; protected EntityQuery NoRotateQuery; - private const float StepSoundMoveDistanceRunning = 2; - private const float StepSoundMoveDistanceWalking = 1.5f; - - private const float FootstepVariation = 0f; - /// /// /// @@ -260,7 +255,7 @@ protected void HandleMobMovement( var audioParams = sound.Params .WithVolume(sound.Params.Volume + soundModifier) - .WithVariation(sound.Params.Variation ?? FootstepVariation); + .WithVariation(sound.Params.Variation ?? mobMover.FootstepVariation); // If we're a relay target then predict the sound for all relays. if (relayTarget != null) @@ -406,7 +401,9 @@ private bool TryGetSound( return false; var coordinates = xform.Coordinates; - var distanceNeeded = mover.Sprinting ? StepSoundMoveDistanceRunning : StepSoundMoveDistanceWalking; + var distanceNeeded = mover.Sprinting + ? mobMover.StepSoundMoveDistanceRunning + : mobMover.StepSoundMoveDistanceWalking; // Handle footsteps. if (!weightless) From 5afef035f9c1e5e75d1327496cd657e9c69a861b Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 8 May 2024 10:23:05 +0000 Subject: [PATCH 042/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 87acc64faf7..1b771c46dae 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Rainfey - changes: - - message: Disallow multiple antag roles per player - type: Fix - id: 6056 - time: '2024-02-29T06:25:10.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/23445 - author: Kukutis96513 changes: - message: Changed the amount of plushies in the lizard plushie crate. @@ -3851,3 +3844,10 @@ id: 6555 time: '2024-05-08T07:30:53.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27734 +- author: Plykiya + changes: + - message: The ninja's katana dash is now more reliable. + type: Fix + id: 6556 + time: '2024-05-08T10:21:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27793 From 3fb596df13e76ba3261311d1a43294ad8b171ce2 Mon Sep 17 00:00:00 2001 From: K-Dynamic <20566341+K-Dynamic@users.noreply.github.com> Date: Thu, 9 May 2024 00:30:43 +1200 Subject: [PATCH 043/215] Bike Horn, Clown Recorder, Suspenders for Theatrical Performances Crate (#27668) added clown and mime item to theatrical crate --- Resources/Prototypes/Catalog/Fills/Crates/service.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/Prototypes/Catalog/Fills/Crates/service.yml b/Resources/Prototypes/Catalog/Fills/Crates/service.yml index 70de01316d7..3b52faa0c0b 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/service.yml @@ -94,6 +94,9 @@ - id: RevolverCapGun - id: BarberScissors - id: ClothingUniformJumpskirtOldDress + - id: BikeHorn + - id: ClownRecorder + - id: ClothingBeltSuspenders - type: entity id: CrateServiceCustomSmokable From 3b1ba3b0a2e4ee57590e8b7d19d851a28735a03a Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 8 May 2024 12:31:49 +0000 Subject: [PATCH 044/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 1b771c46dae..56661b6a021 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Kukutis96513 - changes: - - message: Changed the amount of plushies in the lizard plushie crate. - type: Tweak - id: 6057 - time: '2024-02-29T16:45:04.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25122 - author: Aexxie changes: - message: Nutribricks spawn in Survival Kits in place of Tinned Meat. @@ -3851,3 +3844,11 @@ id: 6556 time: '2024-05-08T10:21:59.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27793 +- author: K-Dynamic + changes: + - message: Bike Horns, Suspenders and the Clown Recorder are now available from + the Theatrical Performances Crate + type: Tweak + id: 6557 + time: '2024-05-08T12:30:43.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27668 From 21f0eb01490775a1b101e5d9ce81e9193c11fe35 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Wed, 8 May 2024 13:14:17 +0000 Subject: [PATCH 045/215] fix mapping door access (#27784) Co-authored-by: deltanedas <@deltanedas:kde.org> --- Content.Shared/Access/Systems/AccessReaderSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index 89c08e0a4e7..3670e24bd32 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -153,7 +153,7 @@ public bool IsAllowed( return IsAllowedInternal(access, stationKeys, reader); if (!_containerSystem.TryGetContainer(target, reader.ContainerAccessProvider, out var container)) - return false; + return Paused(target); // when mapping, containers with electronics arent spawned foreach (var entity in container.ContainedEntities) { From 2e8bbe6a341e9434d7c75c26d744036fcffd4712 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Wed, 8 May 2024 08:54:17 -0700 Subject: [PATCH 046/215] Fix Supplybot Ghostrole (#27811) * Add raffle to supply bot * Add GhostTakeoverAvailable --------- Co-authored-by: plykiya --- Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index b405a67d069..3c61306e041 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -339,8 +339,12 @@ layers: - state: supplybot - type: GhostRole + makeSentient: true name: ghost-role-information-supplybot-name description: ghost-role-information-supplybot-description + raffle: + settings: default + - type: GhostTakeoverAvailable - type: Construction graph: SupplyBot node: bot From 8b3ec92c19876840bda984dfd7405cefcf0ca459 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Wed, 8 May 2024 22:54:48 -0700 Subject: [PATCH 047/215] Adds supplybot to crafting menu (#27827) Add supplybot to crafting menu Co-authored-by: plykiya --- Resources/Prototypes/Recipes/Crafting/bots.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Resources/Prototypes/Recipes/Crafting/bots.yml b/Resources/Prototypes/Recipes/Crafting/bots.yml index 9a70a19c868..3031f4a7803 100644 --- a/Resources/Prototypes/Recipes/Crafting/bots.yml +++ b/Resources/Prototypes/Recipes/Crafting/bots.yml @@ -62,3 +62,16 @@ icon: sprite: Mobs/Silicon/Bots/mimebot.rsi state: mimebot + +- type: construction + name: supplybot + id: supplybot + graph: SupplyBot + startNode: start + targetNode: bot + category: construction-category-utilities + objectType: Item + description: This bot can be loaded with cargo to make deliveries. + icon: + sprite: Mobs/Silicon/Bots/supplybot.rsi + state: supplybot From 8b491e714b7428cbe396d47bcc4827160eb36d2b Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Thu, 9 May 2024 05:55:15 +0000 Subject: [PATCH 048/215] fix mech energy display for 0 (#27828) Co-authored-by: deltanedas <@deltanedas:kde.org> --- Content.Client/Mech/Ui/MechMenu.xaml.cs | 14 +++++++++++--- Resources/Locale/en-US/mech/mech.ftl | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Content.Client/Mech/Ui/MechMenu.xaml.cs b/Content.Client/Mech/Ui/MechMenu.xaml.cs index 8d1d9360318..fad76488086 100644 --- a/Content.Client/Mech/Ui/MechMenu.xaml.cs +++ b/Content.Client/Mech/Ui/MechMenu.xaml.cs @@ -35,9 +35,17 @@ public void UpdateMechStats() IntegrityDisplayBar.Value = integrityPercent.Float(); IntegrityDisplay.Text = Loc.GetString("mech-integrity-display", ("amount", (integrityPercent*100).Int())); - var energyPercent = mechComp.Energy / mechComp.MaxEnergy; - EnergyDisplayBar.Value = energyPercent.Float(); - EnergyDisplay.Text = Loc.GetString("mech-energy-display", ("amount", (energyPercent*100).Int())); + if (mechComp.MaxEnergy != 0f) + { + var energyPercent = mechComp.Energy / mechComp.MaxEnergy; + EnergyDisplayBar.Value = energyPercent.Float(); + EnergyDisplay.Text = Loc.GetString("mech-energy-display", ("amount", (energyPercent*100).Int())); + } + else + { + EnergyDisplayBar.Value = 0f; + EnergyDisplay.Text = Loc.GetString("mech-energy-missing"); + } SlotDisplay.Text = Loc.GetString("mech-slot-display", ("amount", mechComp.MaxEquipmentAmount - mechComp.EquipmentContainer.ContainedEntities.Count)); diff --git a/Resources/Locale/en-US/mech/mech.ftl b/Resources/Locale/en-US/mech/mech.ftl index 19f570a2a10..9d4f7ef0e07 100644 --- a/Resources/Locale/en-US/mech/mech.ftl +++ b/Resources/Locale/en-US/mech/mech.ftl @@ -13,6 +13,7 @@ mech-menu-title = mech control panel mech-integrity-display = Integrity: {$amount}% mech-energy-display = Energy: {$amount}% +mech-energy-missing = Energy: MISSING mech-slot-display = Open Slots: {$amount} mech-no-enter = You cannot pilot this. From 10e819c288c54c1fa551b2ac4c0538cbede56d69 Mon Sep 17 00:00:00 2001 From: Rio <110139251+Riolume@users.noreply.github.com> Date: Thu, 9 May 2024 00:56:13 -0500 Subject: [PATCH 049/215] Drinking from spray bottles (#27815) Added drinking from spray bottles --- Resources/Audio/Effects/spray3.ogg | Bin 0 -> 12266 bytes .../Objects/Specific/Janitorial/spray.yml | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 Resources/Audio/Effects/spray3.ogg diff --git a/Resources/Audio/Effects/spray3.ogg b/Resources/Audio/Effects/spray3.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a9f493198c78e125bdc69bb0b6432a00cea36037 GIT binary patch literal 12266 zcmaia2UHYKv+t0jNR}il0>ToMEDI7PE;(lvBCPH&%{uIZ|-s$bPq&sk-2b9Dd}_~%N*{5yEgMVE_0i{fVQWNhht zGle2q`p+ZoD7S+;6y=+h|8?E0yrD$H&qN4?uK%yA5A81_Hju7k>0t3l*~uJYV`;2) z%N`;J;pXJx=H%w&fv_ms+F3d{nL3-=xj=7tqhS193YB=Kg#uuMGh7OgF5l5;O#mPS zfR~I=f;cldXdWV)<#iH5_GZ+@8JdI$?V@-U&ei>|4#I0f1_0=QFAGj&&bpk{FwC5S zInpT$W~n4l0Kp1VU54SbA3uC$mtT`_Z8r>M!oH)60f2T9lcf(NRlDhoL+FDjz%H1^ zsh7SW%?X#aFzX#IcQ3?Wf%`{poRZ*=!kkFKEtZ;52|SLPakv847p+(2&6tK>&cm>c zw0}CBTRkA)UL-InO$a5hZ`g-G^OKxFR)5uk0KTA_fJ_XjTovg+70u8Xqw+6yRT922 z9w~JVMNRN>HPm)9opy7bcJtOv3e>Oj)~yTFp9wTP4K$(%`lp?FFP+}BTh$>bKo$cm zVFmi(0o8}^un%YfL8YjG$&E=MII$0)u{m=2W>)2vskIg*wYCG*>;u*Kw~v56MZeh% zkY$;Y|Nk^I-2~JBy@^@$vI1hDEqfekdK?+0)EIl5*s*Rc+zWs{6;osHa^jV7=IwEQ z3N{%R$v%X_(2>I3e>vgC?EoOgMbqU-(+65Zjbp-zPup2|+F5ZLG{ucY{^!f@7B8S8 z^yy{^R=(Ig(MGps@depZWMeRQ2VAQ= znzCM4PYRAg5vk0`dX2yQ{((i&^R(eM*f(%R=v=CGJ&qwbIyXt%8^XDn`|t1(1N~++ zL(!f74&oh&X-rC=Paj9?5=9V~#nlVVrHzP`h8Ov(s8KTKxxBm890C9VgtsXES9c5L zzbMX)4P)(R`8dcuz<$$}b_@${S9TCS!{-7~%+3d*I0Ctz;am=rwa!CM>9OS@)Zoy( zzYzsG6=D)Cj!AhNBtc0|{m=q1D*n@P`-~$*_s9ODjU3RZ5;K8qjGIH9M_5xsQ`^l} zH_>(eQ=s9D>(aFM(saa&dqMvlSpO9{02nj@w=sz@4(IAg^H-9<`Rm|+MUEqBPYg|8 z45NHCqvAOG&@W!)L*6kGDOFxYbuz;-GS>-mQ+0mB2|m*aP17%Krn9xC`k%aYYySzD zzhSd9?fM^)a}y#DV38}bvFQJfoGg~e?*fqwaL@%08q`JV-hVm(09vE*6TF~d;{kvY02n|g z2M~riMN8NT>tJ)OGh$@JFta-;0tyIu@L_izxI~LH3)jWMI`9vd)mX?R75pQZo6NVBs~90tlIbWe3KQI9_>DCV3L6 z5mM7>62<9w-XUH-RSnGn63s6fn)Bp}nzc-dB#Meh8k!_hnq-<235uFX+Yw&VSq;sv zA-I%-rp#0I8T3Z1*t|; zrbeG!msl)QEUi8i7N@Wj7Z(-R+7_3W>3`TP{!rXsZC+en+FxB!Qf{*iQp*dAtJ#Z- z*-NTRFN@i?YYU4T%KED-Q_D-XYk%T(ux~dw6qlEmRPWqS8_Kr39k&}ez4D<0`6XKo zMGXyh+YOF`wYD@l#ak_`1MQXl)jziz9@n_it7^J_MHsGVgL>G$aL>>`4Xm~m`doI| z4>A}(gN@kJ)Pu`D+EIgZ-G^+VMgoy}<|*J#i_21rOSY@+*g8tLB~&%N4dYFho*3%h zvs-!+sXra@p?Ir-b)W?l#I?|EPt(XrV>r(b64I;7@VsC3p5lyH&v!ds3a*^E=2YP0 z^hAN!K)JyNNxxV&JsYa801ALfzd@=_$l3*QAynIf#`IE-GkX&-OB?n?$&#I_?c4T(zY7c3e|5;EG&g zeK}}1sk$vkcM3x{1Z35BBm7pzS`q?emFyo2hnL02stu;a-LUeHgv&u|W8n(muHf*h zKs7xZuBkLN8$NJXAgfro4M(@0z5>#fP!I2RS7k^oYpb$uP&iW_g*?0pNp5Y&wN3@M zEV~h2)J3iz2?|%Y=F6Y5x*_P>3Qwgt!^@^7)gnN`E8FHTDQ|xMOLJo@j_xVrvK!&7 zRq?T)@bx*pa2R6T7-SWz77nh^x2exhumTAwYVVQjbIvw=CzE=mH!|eGH-vLk{jnC9 zb$rnPF<%_hteX^a6Sm?ZYQu7tgieDnRvd{z7;}-6f&>(*IVg_JgdB!Jp6eaW@3aJD8Mt6#kwc0&-cYM?k!r6MPM&~{x~ zHw1*<4g`dT4WAn!2&-SzG)D|$n_L!I7qM_st14t3@o4$jarJI znF5EE*W^|SUkGT%aIPRy<1ns`v}f4Rt|YL=$)~s}J;0ajP5luL62RMTkO14Dx#`lJ zBmZ5P{CkA+|52g{th0!;z_)cb1vU2Hs$81*_V$&{KSg@d-`oGF+5cU=|4$=*>oO2> z|JeazpK-B(Ybm4X4JXAN%MngfcQG7d+A_6IY>l<|AMDLp)3+0Fs*m92CP ziqHMnKDK=8YkIKQmbkU{&A~to^hMSUTIeO=AgXRKLLUIS;qP2P^nc9}3U2&`76_<~ zzt165ByORFoej=G?}H#?xg9ZqBb>hl+$|Ubh`~}00doF3had)J+?IC;wHri&rnw>9 zLiKipLw{qg+lT4@uDz)RL5QN=WMPI)#+?2KHoV{3(NrvY;eanb065NAZ9}JeAzf(! z3GxBaPK3sfh}6PhGF7IslwA~G5L|hr>T1@TX~yBI%IUA*TpgtdEjUZeyK$#dq}w2P zqJ&@roP_i|eqt!36uqFJ0CXsjRoObqWq^*pO3aOmkJ}UAJZ^~w5C-(($AswM6O$DC zeJ~aTbPQhhV)^1nMAW>;+vIM=FkEnj1VGT~L%L8%qka8J>^#2TQLbyl5tPa!_QnsF%<=vb2OV^ve`s=){m~;<9!}0jT-;oooTF32 zBST}|L%n@%J-^Y*xO!q*QhZgIZPZbvC6FqdsRE`0A_yL|pV~YV?|=OW$7jt`>_>0l zD|Nm~mDA~S;1$Jxl_cC2o$AYf+{_B1RUt;w8tdB3LBNsgQ(2L~rI>L7bG3)5{!|i6 zfAIL*7w<|i9%To-+$afe$}RjHyxN={MV(XjP!wp9bp8HFs3$j2EyvPZuSLXi<%z8- zjz#WoQ(Hdff6$oYF}CQOFmqF8J;{qRwwuw({cc@!CG?jkFZrq5K#JD%ijuKAMp~ z4A}TDMGR}-Yx{DQB}tYcjWKKTqkitxPWnF^$U8c&SgCFj?6C-Y!K4l zu(fmX$wR_(pPxV7nl7d;9KSaHQsmSxxF=6gFN9gJJNo7Ea^0c5OA{*K!*?{{@q__hhleu~dlz&Rit4 zC-e$p7kMQvFsT2)pRFlGupqS_7~GQ+%5b4}=6|M};=K2#6Ja4=7(5%B)@{S^X!{W7 zl%lw#(jYEMgl?6B$Q@wE?);JNargpFy+ZfMOQMhCdi1AhK)~};Ko{wY57nJ!A*Tac? zbYfVZ$>njjir2i63Ii_LB8&4yH`IFrie4BX8XO~@&v8p?vf6|)@>Q~jbx>cYsppBn zE<~+8zep^bi{7j0XR>a2AD6iLdM5Dh#9Bf^K1s1T9_whW8ZuF75H(1bB=aMlKk#ty z_tUid0NLK8HWu|eLBsjY*Fmo@Y9We!LI-hPm0dsNt$0z}V0QzQ>@Lub(A~8KgOV9( z-qJ-%vbG)!{9sPDndyYRw8s0DIwLJ;wW$&2_j#!6Nb$Y=7z!y$GjUxuywE4}?y{$2`-u4%prVs5IwpM0s)(>CN-68^v_%-p=ltg<3b9w%kc}E^U9( zE<(GMKp!^KgfKU5aa=9H2564^sUs@;$tf<5uNZx?2z|}aws|KJREu)TXbd)#eaFs6 znem_Z(3bC3{iZFe@vzwq^F=}7GA>f_CX>ax&YRYAwIWYnO?r`T=%;&CF_lj7Iau^A zLsZ_>E_E|X__yEB8BT~P^f-N(R8WB6rwoS;eu{AZolJpJGXC~L!@G7T3Nq?np0I7C zU7t_=Kw&&6N890{-zK5oxI`E8&C1$(pX?gbkd&5N5)3)8W#S1c{h`W4R#M12RZ^5S zf1p4pd0f6}E2WyD%0l>KK4>PpRhBcpgfd1`WI_Ml_99Agi#zAoeMyta%^@S(dStC2 zU~p%Ej75>PAx2hh6#oGxL7chUrQ-02bCIZK;%Ifwp-FUbmoV!9KWov)=5d zKGFT@@uBO7bu;0e%Qu#98BBDHlX-cGGGP~9%#AS_@2-Ir#df~cdHk}#9y1?&O~oROf1s%}o~Ah353t=Gs?ur6;JX~(PQ07K3iEs` z5%QjvWg|1zrJXHZS^$QMo`-F0rS-grn|Oa09R)L^iB0R5*eAZk0s82JehdF*y$q*^ zO0q59L?~4u%*!DGyHvz0E4Cxtd{fs(N3zBm!L3pY!yGgZ)i8uTA>IDEg=6wnbC55$=FW-FFgDm^8UQ&l8b?3St!=`~WcQBN1=9cI=V(c4^{5gj!?>0ey&bK? zKgw1FMntu+*jJxF=TkLF%A|cYVuxS-ww$3@-zKwJvEI91?~?nF1pkCm5lvArvs{4~ zEyHYqkYME@=1W|%Yl69>Z{sMfxy2Ez50yw{v62Qf%pTcgI6L(BCVFw6S8@5&h>~dO zvGLzYyOcf|Ocb2SH)?VCz|^gKvka(^2J}OeLYU1zTvj;Kx7A+JWQ~9E2Xz zo*KHDo?-8v3Sqt^38SB3D)Dqr4y?Sa^x7Y`{?KUC*0F4iX;J<_^f{90NFVt*_#=hQ zX$z85SW0+@DDse`5jU5$yq5ir!7u)pa8IGb99hvEo$Q&`>x)$K%b#+%(RD|-B8q_$ z-vOV51#GHSa=|QfY*C`qmpvBEnElFzKUjNcP)`mgNj}fl*J?Tn9TkKEpuJH)9a@Ov{&Y1W9&@V>NHBR;>e%Nh3}k2^_rLS=KpN9VRV0* zx&!0@!xewdFq$2Gyi>#{*@)r_7nF&zU#4jJGmi zom0CYfXIGt=Q}AA@+RrEO;-y+zNRL9nQI7HCH>T?=d`RB=hPCl|4-&qMw;{DpmaN% zkf>eRdtLd_k9un;V^WhXdLS68hZ6DyA`Jq4Gc8bYIn2JmJS>q;(a_0W(LdwcSW;<5 zXf0IUv2_I->Pn%HG@J%$Sa;-TkMBQ@#(wK-2gjX#=u$q9I(xrR5BC!thprv`+lh z)kmw9;YW?4d;DFp-gD4XSzsD|BE1v5mzRr9W|=ueKA)ddC}}w*mhqU(1Ff!4lB!C< zmE!DOop?H<$Jyu0U&fP@QDyqp=?cld*B)Fk|j2U@>k=3!> zh&8(eJ3GE13e{%b&gOL`;!n8F$26WkJp%@<#1icqhhhMcienintydK}@H}XNf?{=) zCky+%cL1+f4%VE*Ow<@tii4UUWHyL;-q{^X-mNIOR)OI8;4WefbPl%;br&K9$K8R({VGHlZn(775Dh2SbB<|MPv@E_9`7C=VX`^wV zZ!MhvQB%OiE_i=a5@Y3D0<`h&Nf zlB*Ib_X$7iV;-eW1i+C#t07yCu45aeoM#$8QwS{f9SF~seGSi?Hl(OQmk{DRDKNV5 zW`DnLu5EPS=O7Sox@N3_9)PyqW$Jt>GV|!``P-tJH?wGHQBNqXt2)nI9y^b#3v^zx z1aS~^hawIiCZ_iDAy&!Uvs~OoU{AP7#N5;<=UbjGW4Ve5&{3$3PwtF;J0maEdZ8b-p=y3}`3oagxqEQ4G)c-129Ty1YV z`8yqg&G9#0ZP!fEClKiH)?&R#&-_@#e_@dyGf&z5$&`B`K;f;OcQgHt)3niUtDzw5 zo-f<{e6W3}a%-Sh#;>{k6F8;{wW_n8VBM~urkyB#eRKKZrE)|DL8E~pW58ol7k}?n zo`yyH>VAv5vVtbg63$rO5KVz)TO~7NiXh&ViI22I`B5SY4Ah?@at9Neq`dn3m9#uO zL#5@#bC__g_Fc2NH!s@(QGns7l< z+&k@wp-v&5ZJ$Nz@M&MBnrckE%~CVc3}sJ&E}mng35A6x&rejA#7~3w^P7#z-*d;5@^uZC1ukG)}fZOiz7g9aAfuen&1_hFg`+PQo4%Ic4ju zRs|6}Q^(lQX?Z4C#cA}C|DAI{>io4ndebFPR(A0Fr0}2??&lM5$16K z$k1BArZE0aJUJBz|9$^$a~wvJ)|N~`pw@G8zt#EdZyz74InUO2G_hUHAz_)?^B-Y0 zEO$G090r~U+mllDg+`C2`3=8QmT$o(Sg&WWJ|HhWM)K~58@y-o?{aMVF)e?mG|rJ`qgk4QB5j-l<}H{W60SlR*3q z`2UTTnMZ(6;DU*3NVft|8P(?J0(^^orIBI*m8bKCQBhDWcY3}J~B%>ZngO+%Emb>BUjX5%*k_>BflV5|oNyC_4z~-po+DVP==pqhrThp3J46(RkGGz{FG3kwUyvj_LTQf)H`As z864KuT7jXsb7to!)PH&?rta+$*(O+^9#^t$3viM>MVBolk*xTMmJz>zZXGhq+98!i z`15FBEzA$g1n8cmOP>nsqp370kF?jLy!+GVxzG?D#Z=nLMPr0{-?whhE0SuD(Y{zj zF2U=HAL=6q_C{$r20BhUA(G}EYJw*zyjOSAe35eYn@^0qV~fbkI(+p5SW?vWMW5|W z*LevfDNqzkmelc06vE!|kD>R!Ml11e#*&C97yNEqzrH7ulpf5VQxtCRQtsuKBQ5eQ z!yvr-8kpqRnp3-eaqp4)ynr8$8;|rXkVFj#3;=b=pjwGlVITv!;9*Lk;!C??^~Q!u zAT5eOVa-d^@;=#yFViDY+PTd5rr7iSWrmef|DreR9ksf0M8E1)Lv1qb{5uel5qBZ5 zDrbK;s%@Upv_ZYZ)-XReQrEwv9XKB5<8_Z*IzE;^6Q1Bm>vSr# zcueRxBc|ja?!VR;v%MKbFrj#i>hmXyZY^CuJ8v8N^NS|+7=h6Pv{>crvnw73Q5pBQ zbIJu{Y0A3xO&7J7?o|cSNT}`W*KQWl1JgttuoN;q2V^k*cPKt-v8}6p|9i%#T&0$( z-TPK})jg5r_w|!=#NeDUo|btT+969!<#-R$?-6hDFL}i`qiFw01d|#EdhZJiAR}-= z($4o`Llp$&r%np28!d+}4^CZ%9D3kesuuNB(SY`Gv%>6)#XOwLPa=v#Jx_;_pZn&i zc~&v_t(5QcH#_>fd(4?(HN5rx_EJkrKWA^seWSLARaxy>kVq?<9M!L;VL(!fj9Ort z39_yau@BXkO~z|Yj66ow+LywyTlxIR-SXuOpZ`Mbw)T->w#lmu!?vc$NCZ&RYmX)Z z>cgZQH{?4;@w`?>cv1x=0r1gz1jFr85U8{^yR@oaba|t)IGgay{NUf^SG zRuA+*=a_$2Sm`?>s~(UxK@(?Ja@GbnRKQEgUi$I?ZEZy4Kq-nfPQIO-NkF^w)7{oQ z)Rz&Q;g{hC{bZ!zhZF*#TyaNMq>G$?4vERtdGy9c{ENZwL3fYKcoAI!0(EDtHX#9u z5YhgtbC=3BIv;_r)oJxa?+a7bO#BXJH9U^ zq(5*-LUxY}6DE{UBenEOd{6;bpO0MTl>O6`rwZC75kty!`8p%PaD|~_mQfPG$SwpU zkInsy?X)?H+~{~xo3~fdWIb)Fj`dLQb-|B*O~x(6Tj^MC`&ijS>oDp;+~m1Fy4;+* z%;n{Gu)JPKIvCvWmj?a>z=)MwNJPiN^Mdcg(6`qfw5Gn#@T=usVK5W{lQ;j zkvbw@tXoJtcP)azR#TG&cMbm=5O>5o@W+*(`R5K@^HtZx>2{5?jRf~ZQlx2u?m(uQ zj?3}=edhaWoabuKVHulrQ}59CV66J>*((myPM!gSTtS~B6t#=>wMEoFjtW0^NQ4wsVw|@L3 zxl<-M))RFg$`hA_t@nN9>z2>*1E$O>No73=;@_AOX&zG>2Ubn)m{^ww5I@3Avb{9Z zi0K;tJ<>*<|{GD0B*bPII zi`1H+A^BAG1FCfSoY!Lw=PZdk10!aSvDz}M=3PezJ;l#00%7}xRG%vju@&9Ml^aDW z`u%i-l2~Kd`uWwm8E`I?sZgh{{PL*~c+X+|q-f~dkM8+$y7>(TX|L#`03!Sdwxc_h zX&<2y5yH;yI=k9VnEHT5s%(9JNLZ*s_7m;I!5!@i#UpiD z*s3q!;#q_uf9=KN9FZS|UZ22U;?jz3g>bElJ;c>R(6!#3Z?C~d;9|Ib82prUq_Y1p zo0_bR5;fN6&$J<9&8e|d{IH-+Cdu+}Ve5te8`x7^Be$PV7toY*MBx?3zser3Sr-yw zrZ%NpIB@iJKydeXQ;SG}C6w0fVaodU&3Jr*55>6rz;iwmoHwZC&dRuQO%ontm3#_p zILr+-#vk(b#og`f*%t)=&`&5R?``ZUr{kE2VmIV#4n}eB8w|y|{Hm<2kKwWnzQ$*u zictF+oYMNX_M=UMBU`)YTq+gnazt$%bwx<<>gTYDoM#anLksTPEiuVp#1ho#}(>h_FnBlLzCB@n{*1?1ZGr8(+0j7Go+(c z->QbprU9`>Ny$rdl6nTM#pu%VS!mDsCh_)BMUpohG>I`*8cDL}(Js(OFVx~?152yl zLF>~0tU-{Xp7SRiX_+$PB>Attevz}MMUKN|Y;dBK(3_sJkx7UvxlCBBe`}X<9SoI; z5=9TVpXg%&*?C!UR=u=9X2}!nM_$}@*DZ%if^}%@qzhMf{@w{EV@QdY2a)Lwh zI|IjyW{JS(K@cu|*29=mPW>o@pZv6gmN5!VerKu#Xj@Y>`}L@#PB~$%o0Bs9 z+p{X{Y^@YLKnC9t-U<3$BN0Q{@vjqS@sr8XU(ZostQ`s_tsKGb{!cO@oEP_6{`4<> z{n&U{Sw?`4%wp*1PPtrY+$VfS`MOvJZjv9hewiqz2JRlbmO5s?Ns(L8UpieIE}bv~ z?1*~p39vMYC5Lc>d#+VeuU>NLBT>%^rQf-+1vPvi<@V6{9^+(SJ?l*Uvt6!sSgYz@F@0zDVN##BjZWR!P z8clOFeXgyfJr`GGZ-Lb6dw=*eJ4B$kRO#azYuUHf<&>VkyZq(3;>8=P^CFg+!E*`w zKSkX!=*Z9*l`c>Ap-@GNpu&&x+O#%}P4dTO?CxQE&_>2Oy)mMR1rGD4<~%YDEn4fe zdqRw)(U&{vD<@0}+uHf4lmmzX{li74uz(ar_i8L#zDj%u&?2=Xbcp5zV|optfBm&e z>A8P8YPio1;zeTDjZ8Rr`K!m2P6F6Kk<v zw`{b($u#5ZnE7C$3F1H}_ua@0W4Y*^6aWDhO@E)VEGd$7w-Mb(%Le}(e{*$y+53Kc LYWYO5ALai5WeH8u literal 0 HcmV?d00001 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml index 64b3568adf5..cddf7f6075a 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/spray.yml @@ -5,6 +5,12 @@ suffix: Empty description: A spray bottle with an unscrewable top. components: + - type: Drink + solution: spray + ignoreEmpty: true + useSound: + path: /Audio/Effects/spray3.ogg + transferAmount: 10 - type: Tag tags: - Spray From afe534f8c63f2adbfefc79e7002a7c3c6b94b054 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 9 May 2024 05:57:19 +0000 Subject: [PATCH 050/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 56661b6a021..6e45f185790 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,15 +1,4 @@ Entries: -- author: Aexxie - changes: - - message: Nutribricks spawn in Survival Kits in place of Tinned Meat. - type: Tweak - - message: Lizards can now eat Nutribricks. - type: Fix - - message: Moths can eat Nutribrick wrappers. - type: Add - id: 6058 - time: '2024-02-29T21:39:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25659 - author: Flareguy changes: - message: Big O2 tanks and N2 tanks no longer spawn in any departmental locker. @@ -3852,3 +3841,12 @@ id: 6557 time: '2024-05-08T12:30:43.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27668 +- author: Riolume + changes: + - message: Added ability to drink from spray bottles + type: Add + - message: Can now see the amount of liquid in spray bottles + type: Add + id: 6558 + time: '2024-05-09T05:56:13.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27815 From bde4d81e76a2f774a989adf3ccf3a374aaa4162d Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 9 May 2024 16:00:16 +1000 Subject: [PATCH 051/215] Add CanAttack check if target is in a container (#27689) --- Content.Shared/ActionBlocker/ActionBlockerSystem.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index f5ed2df227c..47b3997806d 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -169,8 +169,16 @@ public bool CanEmote(EntityUid uid) public bool CanAttack(EntityUid uid, EntityUid? target = null, Entity? weapon = null, bool disarm = false) { + // If target is in a container can we attack + if (target != null && _container.IsEntityInContainer(target.Value)) + { + return false; + } + _container.TryGetOuterContainer(uid, Transform(uid), out var outerContainer); - if (target != null && target != outerContainer?.Owner && _container.IsEntityInContainer(uid)) + + // If we're in a container can we attack the target. + if (target != null && target != outerContainer?.Owner && _container.IsEntityInContainer(uid)) { var containerEv = new CanAttackFromContainerEvent(uid, target); RaiseLocalEvent(uid, containerEv); From f39b2ebef0b7ffdad516e7d4b7be988b428ff1d8 Mon Sep 17 00:00:00 2001 From: Alzore <140123969+Blackern5000@users.noreply.github.com> Date: Thu, 9 May 2024 01:00:47 -0500 Subject: [PATCH 052/215] Atmos pipes now deal blunt damage (#27673) * pipe * weak * inhand * IT WORKS * inventory --- .../Structures/Piping/Atmospherics/pipes.yml | 68 +++++++++++++++++- .../pipe.rsi/Bend-inhand-left.png | Bin 0 -> 6821 bytes .../pipe.rsi/Bend-inhand-right.png | Bin 0 -> 6825 bytes .../pipe.rsi/Fourway-inhand-left.png | Bin 0 -> 6914 bytes .../pipe.rsi/Fourway-inhand-right.png | Bin 0 -> 6915 bytes .../pipe.rsi/TJunction-inhand-left.png | Bin 0 -> 6895 bytes .../pipe.rsi/TJunction-inhand-right.png | Bin 0 -> 6893 bytes .../Atmospherics/pipe.rsi/inhand-left.png | Bin 0 -> 6818 bytes .../Atmospherics/pipe.rsi/inhand-right.png | Bin 0 -> 6826 bytes .../Piping/Atmospherics/pipe.rsi/meta.json | 46 +++++++++++- .../Piping/Atmospherics/pipe.rsi/pipeBend.png | Bin 3965 -> 621 bytes .../Atmospherics/pipe.rsi/storageBend.png | Bin 0 -> 449 bytes .../Atmospherics/pipe.rsi/storageStraight.png | Bin 0 -> 503 bytes .../pipe.rsi/storageTJunction.png | Bin 0 -> 509 bytes 14 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/Bend-inhand-left.png create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/Bend-inhand-right.png create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/Fourway-inhand-left.png create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/Fourway-inhand-right.png create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/TJunction-inhand-left.png create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/TJunction-inhand-right.png create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/inhand-left.png create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/inhand-right.png create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/storageBend.png create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/storageStraight.png create mode 100644 Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/storageTJunction.png diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml index e5b77095b0e..0025fc5ae1b 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity abstract: true id: GasPipeBase parent: BaseItem @@ -101,6 +101,20 @@ - type: Construction graph: GasPipe node: straight + - type: Item + size: Normal + storedSprite: + sprite: Structures/Piping/Atmospherics/pipe.rsi + state: storageStraight + shape: + - 0,0,0,2 + - type: MeleeWeapon + attackRate: 1 + damage: + types: + Blunt: 8 + soundHit: + collection: MetalThud # this NEEDS to changed to the metal pipe falling sound effect on april first every year - type: entity parent: GasPipeBase @@ -120,6 +134,27 @@ - type: Construction graph: GasPipe node: bend + - type: Item + size: Small + shape: + - 0,0,1,0 + - 1,1,1,1 + heldPrefix: Bend + storedSprite: + sprite: Structures/Piping/Atmospherics/pipe.rsi + state: storageBend + - type: MeleeWeapon + wideAnimationRotation: 180 + attackRate: 1 + damage: + types: + Blunt: 6 + soundHit: + collection: MetalThud + - type: DamageOtherOnHit + damage: + types: + Blunt: 3 #This should be 6 but throwing damage is doubled at the moment for some reason so for now it's 3 - type: entity parent: GasPipeBase @@ -139,6 +174,23 @@ - type: Construction graph: GasPipe node: tjunction + - type: Item + size: Normal + shape: + - 0,0,2,0 + - 1,1,1,1 + heldPrefix: TJunction + storedSprite: + sprite: Structures/Piping/Atmospherics/pipe.rsi + state: storageTJunction + - type: MeleeWeapon + wideAnimationRotation: 90 + attackRate: 0.75 + damage: + types: + Blunt: 10 + soundHit: + collection: MetalThud - type: entity parent: GasPipeBase @@ -160,6 +212,20 @@ - type: Construction graph: GasPipe node: fourway + - type: Item + size: Normal + shape: + - 1,0,1,2 + - 0,1,2,1 + heldPrefix: Fourway + - type: MeleeWeapon + wideAnimationRotation: 90 + attackRate: 0.75 + damage: + types: + Blunt: 10 + soundHit: + collection: MetalThud - type: entity id: GasPipeBroken diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/Bend-inhand-left.png b/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/Bend-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..f4f60cdd179557f334a87c09f05076ca6ada9ab7 GIT binary patch literal 6821 zcmd^EZ)_Y#6~Cf_N~)9=K0qLd^`##ubiK1X`(Nj49bej5?S}M0w|~9o zoKx2}C@7Zh&O7_w@4b05@88V*2M^r2bH{sk)M~Yzv-?|f@Oyjmx$bT7{kV7h_waK~ zG*BdBagnPR@=4c%`e0Y z?YnIn^d<=l96mYh^?|xpYuq;M6MBrtg2OwWKUIJ2>9cjgV^j6RS{t?dGknS0e=_8U zP9B)2Cy!B!)o*L=+|d}?fY9SH5r)04AKAmHdJ1gA|H-ga7gCb=*i?Nwkti&*4+=9u z$OUau7b(&W!LTM}%`jBsRzX3sCLt_gBr4cek*z91{#OTag3t(=YtOar$i>0aRDCIq z`?e$v27}1~o(#f{BwLmxAw^OY5h%pygddZk=tp}pkQ}DPBN}@B*b96ifhA6`98c9j z<7_Iueu?czxxK)0(vb8ec@ia4O9>e*>H5oIH`RbqiFbLA`*8%ca!K1?3gRGI3f6%x zUyld?6Sv!?jIp}(dZmac-nRnN$gMJ#I+{Px=h7UHg5{9%eJh~mo?H(*VO>R5unF3#x?UIoxgf;3s5wzb+g4~Hp=t)bBL;QGUs0?yxz0fCN#FI=qRWDL$QD0=42FTN7 zY!jLk8BLlbFb=aG87&macybQ!(skR|d^6|@`&jtF68A5c_%f7~YAk;gxbc95e7XY} zVk5~o0~}?mB9GbmTQqh=RyJ*FNJcs*rX?yGc&MSPNHi(aL>+>Rrs|GtFi2K!&eTvL z$VrWn)U_&<2mf;um%EWJ5x>J(Q!11w2Cm!%F%1)8>?)$8SgL3s6_OjnnrL!F4eYqo za@}G!8{PZag5Y_C>|{;kt8q9f6*XLn-KnK~E^FPazHj((iXYN|HDy zt@tc&m&*FDSey#Re|NVr9(o%L02Q4|dk9zl47U>CxB)d7#jc1|Qxi2;H6V9F6^$&c zo0euF7eRfynE-cn`_op|#8s#!n+6dbUBM#dDo#o=sJ<1XDg-g2s+&nVYunPRF7K2+ zU2N#Q8mq>iGB;LYK^g(xAgm>kuMC3)eg&f{WBW^-ZHaML^bEJ+PaErV-gJ!n|M#bC zBUU?`qD@-c1QoqrmP_wQl74a;0~`EujSsXia3*M1>ae2q+-Qhf!@lZ8IW)`9rq*pf zH+CYD=~)50L{PBR_A?VP2Br{In7*}Yg!R)Rlbk;Uffi2No$5np5$~lJCHmCHx9h1YZ``w zkF~?2^as5m?=G}FIFO8zPFh!UjCt@0vc%N(O}Es(B6HVStR@C2(>TzDi; z<)sPt;ie>nE0NdE{`ISHo${;M*7W>v@zvknd&B318*l&U$}``5QM&8p>b<3mC;R`1}wfN_A7YBE}^AA5h`@~a&FMayC<=GdbS04^1 zzIE!MiQPAy{)Z$OpWpNH%MZSA>fDWA`SJrZM^7&vz3vyke(>SP-hF2051zky;;Fwq z-Vsws`1(mWb>``#=ly@*^PPXapWJ=^+26hJ-ShW-;g$OjYcm%<@uLszdgkvJF7$(+ zfBT_{Tfgze_uX{nrHQ{Z|M=KHU+;hYrQbYp-y_d{#@&7Nd=rp9z<&7-*MD6Xe|Y?c Q1buejf!33IKl$1J0Pg`AsQ>@~ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/Bend-inhand-right.png b/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/Bend-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..dfd245221f8af9ded5edfb2937d1c6abd14bb37d GIT binary patch literal 6825 zcmd^EZ-`u16`!irG;KcFG>Hg4ClrkK?fY}ze`hCdcG6_2v)yh+y0J9$zI*SReP(9f zv2~8D^0TKLQTMAi94Mhzon1&+egC7*rfZ9UT51u>k&%T+= zrrX_B0vXtuefOR7JLjHr&$;K^`?Z;(uG-58prbkNpvT z?ucd&>?@u7&4>R25ASPE)F(=%pMCT0|KK~}`L6E4!%?Yp_Xqjk9i?Z!yR%ftk>=Wb*WU@+v{R-l*NL}nto@zeC>so%YskG%a7`Hx!#>*^ZvmT zAv<(px|={bPVW-%6e?N%ppdgJ9jumk_|VW}+iN#dj99j-PKu(+6ycv^>9~G#$-OVJ|QJJU3VdD^);YGVlCEYoj3woC8zDq2XPS1 z2dhBmuLlHxiR<-T#zX!BVuRA`P?fWEaW0X zM5c>HMPXFL#DFhqVb4Pt5!X8)bZiw^&tjgXn41iX4T};DD*)?RiMF<3 zC?a)joq4W>kVe*_t*h&VLZ;)e#^%9NM6in}5y6xswXH_5Xz55*RfWjRcGsaTQy8-h z+=oq#fHwn{YE;FR$FLDZ zuO6&WB{7^!WeSrgR7MTkyC&-8SWB29;R zfK`cQBTYdXvrU`{7y&aR=%CM&-0?!{>6aRZ*s;P;p5*%VLKlY-<2&hiIonFJ`XbqB zfV{sMJDBhyqe}P&#$eV1qnRQJPs-ts@Y|Z^n?M)XN5U`7v(C*nz67ODHIhFHym$$R zY@!J}#9B7v1aOe8h&*EDZ_wBgUR<}SVKXuqwro+=Awo?ZbzSxptH!rHRdG9cNls)2Bn}E25lj)K7}|CaJSp? z3FdiDTI|rYUCQgf%sB;&|Ltm{EcDly017(g_7HCU8Ez!OF%xPqf;bFC?Ehre?Pnrl1WYC>GktBv#(Be8;fJOU^sdlk^((8YQju;L%aQtT z=6EaGTIlzirYgfnQuueuSbROmc=+aw#n*$3D}0~n_;FQDB@GSdJp6PwIgmUAHx8_7 z=sJRfkKy4#`We5+T5~lY4kRPqNgGtUQjPRcXIhV zU;GtE?0-5J!vmMpw;%iM8^61B^{K~|KmXzM_y1kHiduJFKX&Nh-pOBldH4C(F5Z9E zR$n~(wA}cp^Tw_#?C_IsUwHY=A1;4y`;l{(n>#*var;+(`MHmjN?-cGu{ZC0e&(OX TW8Iz89DZv5bnV=}C!YK--72c$^lBSJCC`bjGX0$2Dp&ifT&O7UkAM4rqXa=KwW$!IQd<#KDI(=1s6fH_!=DJM2>$d3Pf=Q05w+r>3KC?_?CgwZ zoptKk1_i~^?t1Q=^PO|gz2|lBmkOgpJ9m71M=F)tIWn9Zhu_}dv;7wMe)dNv{sKQ+ zyz!y_)VWvhehVIMo$AZ?rBdI0;`Xb!1D@|FJXjno7K@9Ei+lF$fq{Fgzxh@`=Z}w0 zEG;didOv;k@9=zEb@;HCO5J{E@Y$03`Zw-Mr9SqsQ=IfC^A8%tEq7zfHEDOfTm|Y> zDs!M-#bk#1j7g`QN>BQopDd;s$LdKRlJk7NIzXqL;bS#Aacs0mj?EC=N*~DX+>xmp zfKaABX6ofq#WU(X=?1U?{|CcdnrV>uGd=0PKq51lFE9gcjWTk#!V+Fl8CCBVWL1^a z`x%iJWR6E1;#mfPd({r->(`RH$Ok$J&(HG+7u_~ zy3X+;CyFdku--z&$91;i?F~ViFgfaxnp5>1x55Ol*mP(8o-}A2PNiIpu`6EFUSK({ zj;ox|%?DF!5LzUrtIpO+4Gk>9(Go4withof5YtwtUElSl-BqCD*DV6T#QA(IqpdFG zaxB902j^fKO{=t}_KFKt%8gUcovjf%I0tI(jb`Kd<8;}mYhVZRnAvyiRd|CpEt8^k zXBE=mO-R~U@?iZlU;<;HMzQbKimqGAhI^?HS(6zUXzU%P>yT5i+<9+bhFi%JY@b#v zn!`TLiacx+foFNKD98qq4MDw+7Yv?HK!lVn2zl3X?1cyc0XxeJMPQU9L*_plGAD>y znZY$I?Bn-iPm;v|7BOtMR>pqTDdQ>1RVz~&Zh0`Ki#L-o;FjDP=t8q7!?hc3!;^E# zY_Na(DujAqb}wFxru+2csl-rou;gSz|F>M?SjMs}0SOv1k-{R?#H=7ti^W8NFH6I= zjW8m*n&ARB1c*qT#&R{sR)MuOYHNbJ$*@?_EFxh6U~MhbR#y~(wM<>6wy7Z`k#%Tm z$}+Ja(gm=}=D}KsU=vw{1yhoyZ6$(PO-7<93WTS+xejfqK&htS25i#^cr#!v$r7<< z2ik5TtSt&6(m-2DQ6eMoHb}8~DkDTh)wI@O1e=OT6hKi}GIc#m24Se%0#`6i-XxYS zu-K+LYsr$$>bl9Zx?=J~(j=-0avS}^p`@=RJ$Q8|4I*YfZ;VttAA<*v#DCZtt14DXW^ONGujCAvBz6 z@fJ)PiIOIQ8z5MZP|F0^3Nd3f2g(s#N>D@wRSD4ufhpLkEwdJqWY(0xszlR~Bp`|E zDvkuSfmtDF*a?E%LL&4uN{tEng~U)CE-blsChPZfl!w2%TVW3qLkS(&Ns7tKMOOV_wB(oajiHb~{=(Zir zX03ZaTo6>`c_Ua8;bvSD;#THpZH+Mr;vI&bjzTGD*W5A_SD%dUGPqhTIRpoBj+?7k z&2}lS|Dwf7VElJ?n?-BR8UsK=r`R6MjX%SU1URZf4Mvd7B1w~3*_Kquolr&dI#M)U z)_I$U`gT15?&kKVjjV|+5?Rnx%$kabSVAQflw?qSi@YRa-olc!o}{y~ExqaTPVCds zhHh44$@mlJ#zZV=M1c1RYeD3T!(fD;z(~s2{1RtVV%!Zq!;SdU+WMRi9OM4~{VCjt z$<8KflU6oCQLh)~(g%X1ADqU(2ESh811$`k2^xtyENVU18sb*4FMClF+G?Int=fF9 z?L;KhGXZvuAa|qfXDFf#Oe88YeQw#tal@Gj4owZ{U7^WpR90Cb!(B5LBQ@Sk2CZmm zw$X2@A`gF@5PUZoldoGD4_}`#`MQ;HneU;B<7dUDq!q(4PjI>$9!O5WjRVapvW(#1 zW94uweZi^I(qzto14%FFq!p>jvGPS5w!^EjC0>MVp<^Vj1sW?6j>4S-Ji%&37aqY= zd}%@}a8pu)E0K2=-#!gjI2|LozT)K2!lf9}lr;?@seI(>X;n|~<(oj)AuJv+4j?R(xjb7#lT-`HN-`itLPKHBlA6L0Q4 z{)3k7?y+_ZNyS_4gCA0L=6Tdt) z`{tD+ySnb>X+2PoFIOerRkKez3bKc>e1G1EJ< aC;L$M&gb`ktt+@-8W|kTo$EjP#s2_Xvp0_b literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/Fourway-inhand-right.png b/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/Fourway-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..a59a79716590c16ca73b03a028f4bf46cadc11ac GIT binary patch literal 6915 zcmd^EYiJzT6&_i!QRKSKqiqsX@N{EB>vneDPgZNoT3c4)RTOEHNG&A3bMKwiRJ%Lt znb9*e)Q0{r^g#)V_hnBbc8@LjbYf|=f|q8nNpc5^d-@9t79iAV zh}eaCtr1uY1GyHk1^-9Ge2#6Agfj!V!AK%ISsr7DJfE^^UgNN!>8x(%MOD`o{qw9O zh$=70yex2%Y$<}JNNoEr2jW<^;1kChFCA@*gQtPqbQm@*o}Zte&(F(w&#&^LX_~ws z@sh*=1s5zfLbSj&f_r0-HcW{I*ms+u>owR27TMlxIFJL4#2w! zJTeEJsQhuBU-mH)5kvUNF-efjGI8IlMx&d||Pnkp4S%)`z(=jQ!;I2a& zy@^R%E|2_Q025e4K1HGDS3Iv)jMvgwVoi2vsI_+3&phfjh&La6w!p7tiIz_%7A>KW z79|0ei70S_R1sB6RxMHgoFH0)kb#IPI}pkqah=5kK?ZhC5G%l_z{1tP5Hn|pI+-Cf z2nx{$v1iFr0D>*Y^Xn)qx^+~ge6vw4@T-FIFUuLx4!sX}POCwhpXgP{$DUZH7gfL9l{EfOU*mTT|0Sj@YJ39ovv)1#d!ITU9ZE zOc%jw+XqWzjBJ@;4or#LwpB3V3{{pSNyGv*?M-M)MM@0~wP4#uK-&RJ6e1zRiL~8D zSVs~i*#K=7O-qa*I3UFls4B}?(rvN{BREu&B@q+_m+6~v8Kj{+fl#n*!N$Z9Ipk22 zBdX$XrfCbDso4Tn422q^+C{&3C>v|p2wp!(ql{TDTO*AiL=eFvrDCLr3ySC>(hb3+ z9JYzfVNt=HtzZYT7et{*B!ysz#B11-m}~BM;L zYAl<7(!DX03tAcA1JYWQ`O-9);Ab$hGPb|O*_Ir4L(gz4@wBl%=O&D&cp+vxo1{%z z+XN-OUc8qcjW+%0GzJ#<^%@^&Vc<;A%G6;=>$%Ysw}yQ+irUbmeKxgz^SPlDkyy_R z*foN@t+t=Bh%PXRsKoT8RU4-bXCXQ?wV-!}CaYCh6~zL7%~*=mdNUcdqP5vpzo|YboPZk#TGUikhJ%l_!=3bF z?gFh%mRvZH45Ch2Q`#JBUvy!cT8}OBB4&#NBXcd#@v|MjKs-L4M5{`g<( zxs}q%_ddBeSK49Be5*RV{|B_ZICsnbUvm%4J~UVA`STkuedTXI>nYFVKl} Pm`09_mYz9$;`Dz3>cTgh literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/TJunction-inhand-left.png b/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/TJunction-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..8b14c9117a5c62f47ebb87ecb7885308eb2ad4be GIT binary patch literal 6895 zcmd^EeQX>@6<^~TQYS%(RuC$xtS(hZD0@4*v)?_RF&I<@BQAJH}k$`PK_PhzqS9Kevad|jvg2phu=NXXHzeHf9ppl{|Y}n z!TA2Y+;hMF@Sot}j;W#25XZgnmCgSmeek?x?4k0$a=E;)u&{mmb{M#?{)cBHx^Vp9 z#Ny&2x96kZy8_R5)(;#GIBxS@(Wi&|#y7Wd+=o8xl_$f=(nA*Yt9j!1Hp@4wb)e?B z!eFyb=nM;an@xGOf!rHES;+C8Gmtx^mQbla%%;5q#~N(n*ugSAHbYG(H(1=-Uuar@ zP-P+Eo7GA!u$lw87O(~XN5f){Z;^yE1G%9{B0pIgbo zL=f(qgK4y_(v> zAdTL{q^%{7*1rHIu!b9qgnpy!`;}t6m&Oun^25Wey~FQ1$^69f$G0+PzWs3?|-9*LPVM4imw z8V(7`+p%ZKQUHfquHUGVu;^9E6cg*Use-sPn9`-2$r|=6egkx2MO+ZO4R_%g@#$=| ze}`(61z`3dU5vK->?Ko~q4r?KE5`os_|$W(C0P=d3~Xafz`9KYNn(ybs0Lq-L0lIT zOijHYMs5fYi8`(2YLBe|>l(~8Bz=QniDo!dArip4My#!=X_DaBrpjE~z*wPc(AHK} z>OiE6V6}~dbucA1cBlZRq;1(JgiQz`t zZXm2HOENYW0A*o&Ti-)qlmi6G3oivJ=rIIyT3qk@OJUUX06!Ab2(;*Ug z9ulaoI)ZH|hCncq1Ow^1j;SkA(`ZGnPWHqn+rWur0PgOYuC3~ZtSJJK94R)O?H~sx zjb+7`aX460LVPzqCUbypP}tf+#mfK{nsVnxCVGj);( z=mN7$&;c(Baz`_vr&Vf9u+L_O(jeEW7wV)Duqcxbf5yhxZB&G*0UzAOjXIyiC}9o7ARA2RFXmUEh9xH$RUcdmZY=1ExqmXPU_RihHh75 z+4z&@#!M_|MS!;nYfiV2_9OM4~{VCpv+0G_u zla@C@Nv{{@(g&lYADza)2ESS311$`k30j#tENMMg8{(F+FL_ZL+G(Fnt=N37?nETk zGXr*mAb-8>XDp%%Od={V{m7Dy(}uGU9hzFuyF!!Is;r7qLA+rsMQXj7j9SskY^&eY zWdwhs6n(cDv#&cD58s?I`?`~HiSLs&FD%M!Ny~;)p6GNpK9HP%8wXa@R29R)$MWG$ z`Z2G`Dw87~97qOHC#@-Mj^!`9upM5BE%PE~iyb3#Eznwtcogm&;0abMx$uad(n}Lo zgPW2DT#3A~@aHGtI_2-9BSYoM{eOG;#rHmjw)BpjSzLT+=1jQrh2GyP)hFJ%bv;Tr|KVg{M_~a*PnRh z^TV$m@43?d?e|?8{=&B3z5J^?uV1>*|A{MaKJ(f&p;6s+-|?*{PQQ8Ak$!Km_wmzL zCysoucFjEh{im<`C*HIB4EOP#zR92V|5&>4$ES;zX21BEd$#>%PxIc#xE=53fAEjp zuWstQHgwHA_pC8|@-MISJhS;r*UwL!`}e(jUfb07`Xg6={^Y4sd-+2#;P(RaT!*n9Eell%Xvp1-)+{YC!kvx8rS3nz{n-FI;0xxGg|_a7!W BGZFv* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/TJunction-inhand-right.png b/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/TJunction-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..828920b400236e34ae5a502190109c1cf8a9c2a1 GIT binary patch literal 6893 zcmd^EYit}>72beJ(>!RPph7^(WQ(G{#`At=y{Y4MVoTZ7*jvS^M3g)C-m$0I-C1_V z_S%5tl!|vBKiVKFP>L#{@`IA9RayzDjuf7vR76O;ghT}hR0JxoibxGI=k7c_>#S4P zHYido?XKs}Io~<=oO{ka=ibkc967XO>#nW2TyDqkP-zr??`EGZ?}YEKy>RL;@Utlx zJ#--V%qt&$4IX+X`pf;f+_R5tyNuoi&)Y{1R|YGU%I@8}7Zw&^;6~^2FBx4pdSq;I zaWQxI9pC&H8*U683v#(_H?z;C+!J5@KrZ*e<6dPv94{ZXv0v*&#CK?KtJVPOT&}Rc z)j;?p4LOHSc=f*g>)%<(a~|o-AJxiYxiLT|y`i}#9h*B+!E-0EMe_TLJGK^DHXzh! zh`3g*S`X}2U%m}&!~bkp$a8Iy@MK@UpGoA#%Ol)?-=v(@tMgdY4bHH7CCxBY<1S7X zB~1_&K@oXbu~pGlWiI~9gE)>WG>L1EmJY_^;HfV^8HNp85N2j(dS{egzd0dDmSqW| zEXXns6nrpS4^fM+2e(EbF-(aDxal=Q&#!X~7CHV@*p~;5qp8#yDRw=G?FE(-TBspN zy&{`hn~>m?t})fDwlyGFpjBF<^)LWhDWz>p`k^08`YS-EuR8>QiOc0wMps>GwNyk9 z4o<@~Vykqe4l1(^DvZ*=pK4+{I1Osvn#?8$N9mGLSHTYCDRbz#EAX;69h0IhZv|5J zCL(RSocX^1Ca?#Z6or1X;``NNw3bE^YjOhv?X|<*cGRmAe2WRK?=5v zg22KMAQE-j?uy4&fOSpknv$`>ut+xvR*?j-t{G`->AJ)d$I__ln2Ms}HE8Q-8YYnG z7_7c=utdSgQ3&S2lsLAniV<&WiY&_#7OCZ|L0c+OYU-#B8yf*_1T0aBj7*nlyMeH- zEXj%q+N!#q7(sMFiYro0QLt<{WDQ1esjSEnC<-n!*5WcqL%jq-!Er@rJBl4E+h*&jMYD!uc{i304tYss3p_Shf`>~*ricfM7!oo}(V{$d zh{9t@#k`|p7qS;bp+sbrV2H#k*prxyffLC9+&wZ0#X4rI2T4SV4CfG$z@!yfHDw3` z1^f|-$$%{rb5GNQas*W~6l@;Mpa#Y!#v0*W!_|1As2a=ztcp!bQ6)vCmVpuhU0{|8 z8uD0@o6V%2cBwH&Kb{#%lU%!AXrN|5*-qM*PqxyezKC`jAa8HRHo~mPC}OsOQJ8hd zXrf5O6LWZ7{I;t3M$j4duJALHw0^aXFG6Wkb>$CyH=IFD+CKq1#A-I<2(Xhai`-@9 zuhZB8nqIT1VKdSxGA&-#AVLjY6?qdAjn^T`XsYf=27wTHd!~j8L1Hxqt7}y#5B~2a zPFEwIMD+f`gSb| z?)r|W_4vdzsj6Yufl3o;P@JhMj}$n)P^pVF8EQ7&fR^-a`($}rdfn|iX-p@^i>tA0 z{z>=7OfG0=fVW6%Ec2ylFu~7YWMyo8iL)U&?wX$Adg5tyea>|lPtiimb~Z_yw7dyQ zdc9~bJ;*kFb{Yc<{A!I4v@mccXlLrMr1e~Fid)9M6h$#KiO;51Za!CaA`Wtafos3I-_tw3zD94hP4W~TpbT>MX9D^GNTGTa7frF3b!=3aaUW-=8 zOCB6Z2CS3T)tF=Xi!N-(R$|M%h}fdQ$XpAwJrRw=PyL0>C#l^)x?d|#1(BY@{J@@9LZ=U#q_1XJxcx~=0 zJ0Hy5{J`SQ7vH~q{s)hJ@n`4fE3$d;!q~;0`9psW-~ITvFZ_J-BOkftwq3t9#gR8= z?VoOb<$FKA>B$>^^ZS2Kai`9l*?iYMm(cw0zV!O$OOM|)D_>gJy=~X4N5Apw;rVZE z`}^KKTQ=?O*>nDrV^8jPrk{HGr8(!^=N>wDX3zcOznDM$(A>nUeGlILq5pjU?DW$& z4DK6v`XBGPIC=bi=O6uJ{@E?3KK0SFbANdL?Z|9s+v vR&CwO?bvtxmLKlB@EN1$;mh|mK4u)9>peaG*yIemOBxRozdiokR{6$5BbCa#&U&+R(OmPx zHVxXNgar;C?YBEXU8&R#_dA51>j(DL`Zgf6c|?SMyXA*=f2^7S+wgxpELDYsBsw=%9gignbIp^&WYFb; zHmZvh>4sofqq1fgs__9qL9!+xEMX)n*jACPDnj;G1#yB<>oV7#X&lMK!P8iEA&NS- zB=vf|(HZ zQ)zb!Y(LEG1(uWgq$A0rD4tqE$Y?>=S?snF4H%Vpi?_KSg+MD8w4H?@3c`h86X@dg zfB-OYvsuU(s!O|FhzO&pC74ELm7&z(>~e=oGdv6yyOd8Yftq*cvk9XazHZblumgF) z9C_|0yz!fXN%6k732FQ$B~6w*UjG`Hz@F@K5(VAaAZXRoy>v3SrZ72~>>c5~_j^7I zdg1$O(ngke`wU|728noGL9k6^BqC*2)@-cVvT+ZRZG=h?Ddhk{Ghm*(oFiah7m+** zjH+sD=wm5!iD-}+T!WE_ydHa*tN>utc7tx4M0KxC=DF1I=WEjXU_n=GCVMhy1zpgE z*Kti6Havu<5zxhW|Bm~VhhX-wSd7elCdhngC>v~f_0<1aKs{!!%aXBdVh8IYHXI_# zGG`*8I(#vcxGpA`T1HKZ-4Gyhb&}=E#x{Xdf)p2V4P&Yp4%>ziT&`e621Q}XjO{ELgrQLe zuHZPxq0E&<;&Mx5n(B&{!$UJb1cM zj}`Gi5sMKSJP(P~(3t3$swonTWYI*1VPNXY)H0LkHOQX(WCol|2H@eI={lNWD!M8X znaQc)9EKQ78Y`-)fE!>~k5tPN*ak5l>3UF(kXDHzH>g3WNhwUhHC#<(Sk**F1*=li z!m5l_ZW$yOFa&0UpkrPf-4~6e7aQ{Y$FGWeHhVqAj8}&$+kIzGf z*h(@^0SDR2$U}Di4vig>rEQxUl9A4dX^Dyk9%|?+5>3i9QHLO-sk$Q@43gCwGc{BQ za$IA?b*&2J!T;RE#crev#GmJ^F6Bz(12^x2n1+ckb`{Z4ELAj+3dxOOO*A>826kL( zxo$q2t?vDFLGUa>cDyF?t+*yM@RxXZi!lk}9f6*XLn-KXgSJpqpF$J}q|<46l*DmP zTJl-eE*14(zBna}|L$&Myz6Z-0F-np>>=FzGu%ml;|A1V6uTl;O-{Y#Ky#bOno)t2i#np!!yjsu0A8s%|IgY-~$!xx7>O zbiSdpYOEZ8iriR=1xW;Wov;>1z9J0f_$7?8jNLDBb|uE$)HB?PKW(kgdBZX8|KFd| zjacq%@-}H>6O{LQX)ZkyC;j*|1~&MO8Xst3;7rgi)nR$-xz!N2fqmVJGH8~aO>NqI zZtX-Q)w2Y4ouFW+?Pn@t2uv<2H+^H>#zn(fiw{k0=v|@7N-C?mT$8RFE0B^mb8#!$ zT1@&)LqYH-$?2bS<=2CZ>wF*ey{N8ak~Rz%Jn`vndLVfUZX9@B*E9?V z9~*}U=}&rn-kNK8a3C4RowTlI92;K@VLQDUTj@p0mO4i1S|C}8bQJC!;0abMzwn5k zic1sj!%aySu0*ap{nA(AI_0O+jq%z3*_VHP{B55T-a2(}@8YAsl8)o4cLWocp1b4c z{%a54`_S?yJKwq^xqLy=pUyw^$kku`bLP3P&pi3Tix)ogZ7 zd}8I$Cr0iXzx=~9ul?=H-Y@hP|MJ8&`{I39|N8RYkN@Z=-#+-%7r*x1gA>mlKRofB z{)=?-((MUs<&{^ZGtb=lk1v1!=(Vr(|NX-Gf1RFw&wao7!z(WxTs^nv(lO|x5iBEm@Kea3y*8l(j literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/inhand-right.png b/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..6c8c55a515cf198adc7dde1072a354c7aef4f884 GIT binary patch literal 6826 zcmd^EeTZCF6`x=l+>}sh3szg{V^YCt-@K3ezL}l6*-5&ab~fA1mTXi)@4NTD*=J_v zP3FDbokVCsL!uz~hc*QfK`RJKTdP4Riqh;KEd+`pMXFFJ(t?UMh^-ccdhYw$HYw)ux zTzKF>>6w@B`ZGN2Zca6(N~Pz%ar1xhP4GO?ojD$sN;lueKf6lb{>~kx(#KBti%Zc` z;~|Fx?Fy!W%PReL7pP06>izvLCMQ`WxUA`SCd+UA;@4%tr<3JldP8n>r`fVUbEe0R zo|#)DXHF8EmhZ3a+gt5BfY4?U7W(a0Cv^Ig~7Gjd<T$H%-mFPf%q=mt-U%SyYju$&RK9>0cSd2|~3;J!heQC=~}!ljY?o z>N=9NwzgJTLzSS{loZ>xC0UhJRRjt#Jl%D7h z19{FI`Q9eH{LR3mSl{1-l)p(xW0!OPSHT3%bdTXE=q(08tCp;#`OKQa^mM#-gijsw zJ2Y4e@2yJ1EPVM4VzD}oSWT5-nJBU-tBZ>6Al*^S&&Y}+%LRypasZ(bP~SV9As}EE zWn~cO~MjAc`aI-L!4z~PS68|(HK6N%^DM+!9iwqH& zE*2GqQ4tdZzNm#g4`D=Xvnug01c*$X*j?$^Ca|8xJWDZm7#15AB^p)$*0U0AZNpGR z>e@Qk=$h^VGZw_pU1sYq2oQE-{L6_-I8 zs!#|8*OgsDJw?PGvqh?Fo@m>yEZT-E6V1|?rRXE{ONNTER*c}ygOq2?M#Guygb{`a zo~_roB0eZ$D%iASn~B7wNF<6zL{}poWG{$9g{m4Q5Q$f@XEB)qXOaPUbYxOS48m6r zR;ZE~&ZRPiNh4LWR0snE{z$|Wz=p&e>G@EO;8uZx&x0G(B*Y>_r=n+ix=4|x!#u#M z#Ilj5AdT53&IF8r84@((^CWk=kb2@$<0v~-7|N4eTrYHSFJyctoh)ZtX;xn(I}MP> zo3VolFEVO`Z(s~&JusRnlJKM){s_N~HQxlfz&;XwZJBkhweck=G1W-^Fz})^++$Ns z*dfN*j1#~?wj%O~mA_46hj?|%riRVPVA!%nRfh;Q4NVp;LUqxAB%^DFtC$o*b&MWEppwjMk(~iEQBdb%+HS3n5PxUDeh^Q`TT}qevGmCKD66 z9y4R{Am3r==@^uPUN2}1dG#qofq=W+mQOIx zbJA*urtMN*|7Ff8VEk`a8)ZFz%mh%-DYu7k{m*ba367aigAwG3NV9ZN_cRmsPNKX*E{NKl$ER$OUl*c%QV!GhdztGyDQZQO3@fI6IQ#uIm|YC!WUZb8f(R zN)}?Vv&q_|;U*~S^^(2x5a0CqX$&mzYc)R5!oZoJQ>eqT)^pqxH^jaXMJY5*&!#qS zKF2x{N%Sm$T_q^kZu^;t7y*-s%1mG1uyNjSR{5c+1HCIWS#f1mQ>xNcV>wd%W{J0= zt(CanG*ub?B!z#cjK$Z3jK{CdSbROmxWV^G$B$}iDrsmq=i#Tj$${iixN%@LL)Q@; zd<+i{($D*S)>^9ja3C4-PTJ5?j^P(0*p6?;R(O%HC4o`67KlBOjKZA*Jb|~e3lIL3 zUz)HE+?4d-O2qu$m)?f!lvif!Q;Yo*e|z=vzIEXvXYs;Bd+z)H-sP>m z_tv>vW{@#+?%31%uJ{7xn}_c`_1<5@mskGybLZmOSML1o!ll2Q`_`jRKm5dR&j0@6 zmG(~!|MA<;pTF|MC%^Kf`S=%)TzTi!yFdNH=lk#ec=xlfztNm{ZT)g*;^9m2*Mk>- z8Do|#_AgD}`-ywr!4KVa&tvnK)-U|w{I4n>-SfMf=I*W?x${q7zxDRVp8urv)LFKE z`K4Pg+#>BiEZ=tG@Atj%h5qwT?f%*8ZycU@bN%uYr8n0_m*XXWIZzj1H;_yjcNS%Li60G|+7 zNl8f$50ARKy12Nw=H}*IyLN?ygtWD_$;->j$jI#3vuFGE?T(I)GiT16K7IO*9Xqye z-MVGVmi_zp@7uReR#sM8T3S(2QA$cGGBUEKr>C>Cb7%d|oed2QNl8gSD~x`4x&SHp zk|4j}|9AnzS;;a1pi<5PkH}&M20bMZW?c2??j)cu@;qG}Ln7SYPJ7+gtRUcakTLM$ zqrG=$zyCkOT4}4ciIDNxW%f3iYt|MWa-G><@9UmW^C+&nxv7QOS3xCkg4fiY#m5aD z=ZQ6`BrFik2aC2EmR>Hqb)|KR%>558OEmAS{IZULx85UoVuAI$J8~0+l$fm3{~mD5DT zaB^>EX>4U6ba`-PAZ2)IW&i+q+Qpjbk>oJ0g#XiuH9@>db9hFq2haTd0IAODYmSK5 z6n}fPUE>?q;3PLamI|;^Uk>Ctv~nl;V;cHS{at~LG+e0yI$_MN$(zB%uH z_U5~zUDy4qE6kCu0qM<0*L52|_MLj@c}4ay^UIB%j(t4;Jo|3XviCZ7*FwTm$y}Sd zZAH%*h&zR5JeK%mek12uKb22mivz?aXm&os*lHc#-Zt22o85NlaoE8UgIg|hbZ&rw zTq{1i>V|?5^> zy4D`$?km>igwrfTkdtpl0f^m~Pow%*Kjw)y#}lv~1f#mSu)%ip(?pN!Ld^lUe{G`s6dvRNnwAgb06fC~&AnAA^Z8rkG=iHMZnaNHL|9ljNwDeGWP1lyfe* z=2m2J{(W#F0iG zWz^B8n|>1hGtF#fnRT{h7gSnd#g$fGW!2R-T-$z!9e3J!mtA+eqjsVCikf{x?rYTS zg_3N;SBT3v@EnZrc}y|4<_@?k^~qcS=XXlG zv#{-R;>+)nnPax?U-n}D-a9`t)bUOoxmFgkW7~Oe3+Jqx0A7>f~8lIUpmUTxZ`?9OM_?0tvjmOk*$v&HvDKalUIskV{x+?h+){{gbD+&1D>lTi(4YGP@&au zojI1X*ce_OAxhS$#E~ehXhXb?CO%!T*)J@zh9IsIpDhg492Tx+-MT)xd>q`!ZLXdh z>!P5Wyh7eBcu9 zZtJ;8;}*Z-{@3s&sS%s3tP6Ktf=yHd_Ry8X4eZ;1q^ut`GFKY26US++>?s(78=V9O z1#q+Pm)mzkok+IB8ezc(Rog1Qf#@fb?h`GZU%@{?FPk6{?QpW@&gkp1@W0>rZ}PMw z2RBYFgGWa9Q!(t?G|OPDQz>s*;j46c?gK z7D~cloAgg-3gev6W$zIYh1T&>$F`=CM0zLKQA6ub>QQfDLJ|Xp)K2wM0a+3^jFfC# zMP4;jmkj^Xj}dKMFhm|d4H0@$+h`I)grTU$5j~MuvwwCpy0X+R^f*&<6|Xci+sXgi;$Xh34$sBGQsGj@#h0>Knf_)= zy4uwK^3Z{gh>&JXuWC%-><^+so|(!p2@CUQaq&~-|0xSz_1&iE6beUPBN~ZyN*fO8 zs>Ml(8dnw#yx#m*ec0z_h`J#RrntandI4{E809o!g&LVW0p)8Vb4%xd5sc9}^e$dr zQnxZibh+LcgFsnXOR=H#LWEHQVz&M-Xz;Ii^xKAhQ6(tVIYZxuQcB=UR^F7q9?24= z5A_L!?Cho3cA2j-o20nt7Z6%Wqc)q2%aeLhvZ2upeZF3LGx0^0AR}1E@7EB78dxWWw_$8$K#{tcXG)&j2 zcDfWl$)6KJg7TzL=8yW27=K-&IsOrm%+Fgtg8^ibqe2z}aW3VAKQItx75rBA?E`nuyqELM7|$;5<%^ zgomjn%}X05ofYh$%Vw{+^(I@Bh1IiHWNEOfXg44&BMXy8X8DgAFuiEr8yTEevI@JC ze)TNfcomnGpwj@}*{<6g%MNeIi@-6&f%A=m>r`ZV;~779+p5q(M`O{Px0|=kiaLzH zlCiLYN?-ye(SSq2n*iqK?1+0XN?~^s_3at87H@04ZrxERRYwZ(?mcCcmmpmd98y#? zpuEt4nBqL@uPG&pu3x9F@1pBtt}#k~KZ1qYYiLsGr@Yad@~>f%x1NsX{leQZjvu}_ z6G9@;X~jfbTrZ>M$1kG@L%WOkH|fiTjLZ| z(DjmP??Y1AHi)r!)^r5H%~$@Mcz9Yobf^;}=RnuzDpohjWKfA0;3scC-EVf?(o zF#o0#ISuunzdr>+GwsB{^HfppzG^*dvph z+bgc*5yxHLQd>gjr^{{sHc25bS$ z{14byDcw83YF_{V00v@9M??Ss00000`9r&Z00009a7bBm001r{001r{0eGc9b^rhX z2XskIMF-^p76=X|fYSJ>000AENkl@Wg7`q zQRT_2$#p`gR;!4jD6I$`$N9E8 zbQ}l6;Sc~YnM^=BoetXVwp5J;r4*d=h5mdr8sX{b3D?)xxWB(&8H6ARWE4d>K0e05 z!2tk(5Rz7jQc6@R6-X&Do6SH7L6Ri!JP$$$06?qNLbKTc3TPn zr8IS`JQ-tS0AxKt_dueQ0MrIksZ`7i0nRyG*EM$!P;Nh)&CG6wRP|*XfNa@mY#N+S zr|>+_>^(pTVP+km8VUdy4u@Lj@}&j9IY+D2GB*qK`+YQw^Z_?FH)iVt zE-x>wK47ht;5yCf17NT|KuQVU_c5Q(v9q&dW(X({#_j<{b)%&OAcRPVH%9IODh(1s z@cQ~{ZU|&Ef^h&CV~|p6)c~ahz!<}PJ}<-rm~K;&=lY-Y2aA5q!^1;_VOU5SWIBsK zJ3GsDkJ27w=kox*=o7w+clC^FwOWifExY(Aim5dvwzL!Rco79pP;PU?P^Vl znXU^xKR@H~@iBGlpHkM}^Z9INm%S$Y+jU*My}f}_`pH88{Smnq03|)SySw`@#8N9u zgN!k3ifN6<^D4?V4uH}6003s|1ByD3C?x=^4=C_Nrj!7zKEUb&tUkc%1FSw^L-YY3 X-_^u=F}^#^00000NkvXXu0mjfO~0Yv diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/storageBend.png b/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/storageBend.png new file mode 100644 index 0000000000000000000000000000000000000000..39ffe213efb7142a4979c79f1dd7fcfc0a12c6ad GIT binary patch literal 449 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0p1o(uwN=iz~%gcLsc*MoUwY9Z1H#hIvwQJ9wJ=?c$pFVwhU0q#BNXX2Y zGi79C9335{rKPuS-MVGV7DYwH$jHe3`}gnLw{ORe9YDjnSNGQdDT$IGzu^Da0mE5g zy;ndroCO|{#S9F3iXhC$9{uM(Q1G6oi(^QH``c-E`I;4Y+$MHe&HR18;=j49#KEqm zc{iuMS#-Zu(e}v_Uh%M<{K`%BLK;g~ovKe-R@9|`(Po~BL+&ffoeNJ~b~N|Bz4VQb z>D6Q}o6-x?*@|iRpEfD=HyK@hg>N>cpb9)*e=jx@jigy^sa%e#%aH`0&H5u3s0VZd4( zbs;;h!9w?f-)5#GJFE3wS(G>g4~nz?>sSBX*#CxIW0QOf_a^xhbsyqf9^4UDDRTL7 T=93vHk{CQ){an^LB{Ts52b91~ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/storageStraight.png b/Resources/Textures/Structures/Piping/Atmospherics/pipe.rsi/storageStraight.png new file mode 100644 index 0000000000000000000000000000000000000000..715aeb58249c6800597e1df477b52ba8b12fcbc1 GIT binary patch literal 503 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O_~~1^9%xN=iz4czD#+)y2idH8(f!+O;brB&4maOIWL(^++MrA~|zF+Yw;Ip|6j~>hFSqmJB z6*HoAF2r!`D%<Z+DrE!kc_@}avL=4xm4u_9t)GMw^)_ct2X6#khxDdmcAjzx8lHSictN38+h64{` z3@$VMXKg+$6Z28jVm)OMA?CrRuXQLL+7Kg@zu6#k(K}G&P z^0_Aun^x|b{#W5}hW2mAHkE0A75=iM+H3qiXnm?~!a`-$??>$K@Hk44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0#1o(uwN=iz~%gf8i$S5i*T3cItczDFc#kIAyH8(f6wzlrtwQJ9wJv(;n z*uH)HzJ2@l@87>=%a-ZWr`Ofhg@lAGU%q_i%$X}ztZ;O6l$Ms>x^=6RloZflU#{s{ zKuV@0$S?RmZonYE>CttdI?e)*$YKTtJw*^^WRL#yALwu{PZ!6K2=}+sE*2e95OBRI zA*SM(dm-xS|Np@bZ$vgZx&OYhSABdj;Rb?bH5qiEU6g)TF>sP%rS{-vjnL@<&+j$QRUq5YqYZ ak8O8h+LGPoYjuG^#o+1c=d#Wzp$P!xC*IHi literal 0 HcmV?d00001 From a4e9b5700b634dcc6ccb764b5d2000c331d81b3d Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 9 May 2024 06:01:53 +0000 Subject: [PATCH 053/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 6e45f185790..671fe204752 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,15 +1,4 @@ Entries: -- author: Flareguy - changes: - - message: Big O2 tanks and N2 tanks no longer spawn in any departmental locker. - Use tank dispensers to get them instead. - type: Tweak - - message: Removed all instances where N2 tanks spawned as counterparts to O2 tanks. - This includes things like lockers, suit storages, and the emergency toolbox. - type: Remove - id: 6059 - time: '2024-02-29T21:40:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25660 - author: Potato1234_x changes: - message: Added fill levels to bowls, chefs rejoice! @@ -3850,3 +3839,10 @@ id: 6558 time: '2024-05-09T05:56:13.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27815 +- author: Blackern5000 + changes: + - message: Atmos metal pipes now deal blunt damage. + type: Add + id: 6559 + time: '2024-05-09T06:00:48.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27673 From f76a833997e015bc77cbf465fc274ed6197da626 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 9 May 2024 06:04:51 +0000 Subject: [PATCH 054/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 671fe204752..85dbb7b0b09 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Potato1234_x - changes: - - message: Added fill levels to bowls, chefs rejoice! - type: Add - id: 6060 - time: '2024-02-29T21:41:14.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25639 - author: Plykiya changes: - message: Dragging objects over gas pipes no longer slows you down. @@ -3846,3 +3839,16 @@ id: 6559 time: '2024-05-09T06:00:48.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27673 +- author: Plykiya + changes: + - message: The emagged protolathe can now print truncheons and the new T3 portable + recharger. + type: Add + - message: The emagged circuit printer can now print ship guns. + type: Add + - message: Autolathe emagged recipes that are research gated have been moved over + to the protolathe. + type: Tweak + id: 6560 + time: '2024-05-09T06:03:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27575 From c5ea9839e6ca4d3884b36e5f46d646456776e903 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Wed, 8 May 2024 23:11:50 -0700 Subject: [PATCH 055/215] Make ERT use short raffle timer (#27830) Co-authored-by: plykiya --- .../Entities/Mobs/Player/humanoid.yml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml index fa5b0f5e6e4..b1322a97b7c 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml @@ -27,7 +27,7 @@ name: ghost-role-information-Death-Squad-name description: ghost-role-information-Death-Squad-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: Loadout prototypes: [ DeathSquadGear ] @@ -65,7 +65,7 @@ name: ghost-role-information-ert-leader-name description: ghost-role-information-ert-leader-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: Loadout prototypes: [ ERTLeaderGear ] @@ -97,7 +97,7 @@ name: ghost-role-information-ert-leader-name description: ghost-role-information-ert-leader-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: Loadout prototypes: [ ERTLeaderGearEVA ] @@ -121,7 +121,7 @@ name: ghost-role-information-ert-leader-name description: ghost-role-information-ert-leader-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: Loadout prototypes: [ ERTLeaderGearEVALecter ] @@ -154,7 +154,7 @@ name: ghost-role-information-ert-chaplain-name description: ghost-role-information-ert-chaplain-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: RandomMetadata nameSegments: @@ -185,7 +185,7 @@ name: ghost-role-information-ert-chaplain-name description: ghost-role-information-ert-chaplain-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: Loadout prototypes: [ ERTChaplainGearEVA ] @@ -218,7 +218,7 @@ name: ghost-role-information-ert-janitor-name description: ghost-role-information-ert-janitor-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: RandomMetadata nameSegments: @@ -249,7 +249,7 @@ name: ghost-role-information-ert-janitor-name description: ghost-role-information-ert-janitor-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: Loadout prototypes: [ ERTJanitorGearEVA ] @@ -282,7 +282,7 @@ name: ghost-role-information-ert-engineer-name description: ghost-role-information-ert-engineer-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: RandomMetadata nameSegments: @@ -313,7 +313,7 @@ name: ghost-role-information-ert-engineer-name description: ghost-role-information-ert-engineer-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: Loadout prototypes: [ ERTEngineerGearEVA ] @@ -346,7 +346,7 @@ name: ghost-role-information-ert-security-name description: ghost-role-information-ert-security-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: RandomMetadata nameSegments: @@ -377,7 +377,7 @@ name: ghost-role-information-ert-security-name description: ghost-role-information-ert-security-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: Loadout prototypes: [ ERTSecurityGearEVA ] @@ -400,7 +400,7 @@ name: ghost-role-information-ert-security-name description: ghost-role-information-ert-security-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: Loadout prototypes: [ ERTSecurityGearEVALecter ] @@ -433,7 +433,7 @@ name: ghost-role-information-ert-medical-name description: ghost-role-information-ert-medical-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: RandomMetadata nameSegments: @@ -464,7 +464,7 @@ name: ghost-role-information-ert-medical-name description: ghost-role-information-ert-medical-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: Loadout prototypes: [ ERTMedicalGearEVA ] @@ -494,7 +494,7 @@ name: ghost-role-information-cburn-agent-name description: ghost-role-information-cburn-agent-description raffle: - settings: default + settings: short - type: GhostTakeoverAvailable - type: RandomMetadata nameSegments: From 48afd84a122da1a31bececa731ed3b3433da3cd3 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Thu, 9 May 2024 06:35:11 +0000 Subject: [PATCH 056/215] ninja criminal records hacking (#24982) * more humour * spotted a troll * add TryFindObjective to MindSystem * replace copypaste bool conditions with CodeCondition * use CodeConditionSystem in ninja + add handling for criminal hack * add criminal records hacking * update objectives * :trollface: --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Systems/CriminalRecordsHackerSystem.cs | 7 ++ .../Systems/CriminalRecordsHackerSystem.cs | 62 +++++++++++++++ .../Ninja/Systems/NinjaGlovesSystem.cs | 10 ++- .../Ninja/Systems/SpaceNinjaSystem.cs | 29 +++++-- .../Ninja/Systems/SpiderChargeSystem.cs | 5 +- .../Components/CodeConditionSystem.cs | 17 +++++ .../SpiderChargeConditionComponent.cs | 3 - .../Components/TerrorConditionComponent.cs | 17 ----- .../Objectives/Systems/CodeConditionSystem.cs | 76 +++++++++++++++++++ .../Systems/NinjaConditionsSystem.cs | 13 ---- .../CriminalRecordsHackerComponent.cs | 31 ++++++++ .../SharedCriminalRecordsHackerSystem.cs | 48 ++++++++++++ Content.Shared/Mind/SharedMindSystem.cs | 24 ++++++ .../Ninja/Components/SpaceNinjaComponent.cs | 20 ++++- .../Ninja/Systems/SharedNinjaGlovesSystem.cs | 2 + .../Locale/en-US/ninja/ninja-actions.ftl | 2 + .../Prototypes/Datasets/criminal_records.yml | 6 ++ Resources/Prototypes/GameRules/midround.yml | 1 + .../Prototypes/Objectives/base_objectives.yml | 8 ++ Resources/Prototypes/Objectives/ninja.yml | 18 ++++- 20 files changed, 349 insertions(+), 50 deletions(-) create mode 100644 Content.Client/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs create mode 100644 Content.Server/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs create mode 100644 Content.Server/Objectives/Components/CodeConditionSystem.cs delete mode 100644 Content.Server/Objectives/Components/TerrorConditionComponent.cs create mode 100644 Content.Server/Objectives/Systems/CodeConditionSystem.cs create mode 100644 Content.Shared/CriminalRecords/Components/CriminalRecordsHackerComponent.cs create mode 100644 Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsHackerSystem.cs diff --git a/Content.Client/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs b/Content.Client/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs new file mode 100644 index 00000000000..21fccc880da --- /dev/null +++ b/Content.Client/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs @@ -0,0 +1,7 @@ +using Content.Shared.CriminalRecords.Systems; + +namespace Content.Client.CriminalRecords.Systems; + +public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSystem +{ +} diff --git a/Content.Server/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs b/Content.Server/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs new file mode 100644 index 00000000000..91285a1ca73 --- /dev/null +++ b/Content.Server/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs @@ -0,0 +1,62 @@ +using Content.Server.Chat.Systems; +using Content.Server.Station.Systems; +using Content.Server.StationRecords.Systems; +using Content.Shared.CriminalRecords; +using Content.Shared.CriminalRecords.Components; +using Content.Shared.CriminalRecords.Systems; +using Content.Shared.Dataset; +using Content.Shared.Security; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.CriminalRecords.Systems; + +public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSystem +{ + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly StationRecordsSystem _records = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDoAfter); + } + + private void OnDoAfter(Entity ent, ref CriminalRecordsHackDoAfterEvent args) + { + if (args.Cancelled || args.Handled || args.Target == null) + return; + + if (_station.GetOwningStation(ent) is not {} station) + return; + + var reasons = _proto.Index(ent.Comp.Reasons); + foreach (var (key, record) in _records.GetRecordsOfType(station)) + { + var reason = _random.Pick(reasons.Values); + record.Status = SecurityStatus.Wanted; + record.Reason = reason; + // no radio message since spam + // no history since lazy and its easy to remove anyway + // main damage with this is existing arrest warrants are lost and to anger beepsky + } + + _chat.DispatchGlobalAnnouncement(Loc.GetString(ent.Comp.Announcement), playSound: true, colorOverride: Color.Red); + + // once is enough + RemComp(ent); + + var ev = new CriminalRecordsHackedEvent(ent, args.Target.Value); + RaiseLocalEvent(args.User, ref ev); + } +} + +/// +/// Raised on the user after hacking a criminal records console. +/// +[ByRefEvent] +public record struct CriminalRecordsHackedEvent(EntityUid User, EntityUid Target); diff --git a/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs b/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs index d84a7287751..2c0f6c63e3e 100644 --- a/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs +++ b/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs @@ -1,8 +1,9 @@ using Content.Server.Communications; using Content.Server.Mind; using Content.Server.Ninja.Events; -using Content.Server.Objectives.Components; +using Content.Server.Objectives.Systems; using Content.Shared.Communications; +using Content.Shared.CriminalRecords.Components; using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Systems; using Content.Shared.Research.Components; @@ -16,6 +17,7 @@ namespace Content.Server.Ninja.Systems; public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem { [Dependency] private readonly EmagProviderSystem _emagProvider = default!; + [Dependency] private readonly CodeConditionSystem _codeCondition = default!; [Dependency] private readonly CommsHackerSystem _commsHacker = default!; [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly SharedStunProviderSystem _stunProvider = default!; @@ -88,12 +90,16 @@ private void EnableGloves(EntityUid uid, NinjaGlovesComponent comp, EntityUid us EnsureComp(user); // prevent calling in multiple threats by toggling gloves after - if (_mind.TryGetObjectiveComp(user, out var obj) && !obj.CalledInThreat) + if (!_codeCondition.IsCompleted(user, ninja.TerrorObjective)) { var hacker = EnsureComp(user); var rule = _ninja.NinjaRule(user); if (rule != null) _commsHacker.SetThreats(user, rule.Threats, hacker); } + if (!_codeCondition.IsCompleted(user, ninja.MassArrestObjective)) + { + EnsureComp(user); + } } } diff --git a/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs b/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs index 835ac7ad6cd..1dfaf4f3393 100644 --- a/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs +++ b/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs @@ -1,12 +1,15 @@ using Content.Server.Communications; using Content.Server.Chat.Managers; +using Content.Server.CriminalRecords.Systems; using Content.Server.GameTicking.Rules.Components; +using Content.Server.GenericAntag; +using Content.Server.Objectives.Components; +using Content.Server.Objectives.Systems; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.PowerCell; using Content.Server.Research.Systems; using Content.Server.Roles; -using Content.Server.GenericAntag; using Content.Shared.Alert; using Content.Shared.Clothing.EntitySystems; using Content.Shared.Doors.Components; @@ -19,7 +22,6 @@ using Robust.Shared.Audio; using Robust.Shared.Player; using System.Diagnostics.CodeAnalysis; -using Content.Server.Objectives.Components; using Robust.Shared.Audio.Systems; namespace Content.Server.Ninja.Systems; @@ -28,7 +30,6 @@ namespace Content.Server.Ninja.Systems; // engi -> saboteur // medi -> idk reskin it // other -> assault -// TODO: when criminal records is merged, hack it to set everyone to arrest /// /// Main ninja system that handles ninja setup, provides helper methods for the rest of the code to use. @@ -37,6 +38,7 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem { [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly BatterySystem _battery = default!; + [Dependency] private readonly CodeConditionSystem _codeCondition = default!; [Dependency] private readonly IChatManager _chatMan = default!; [Dependency] private readonly PowerCellSystem _powerCell = default!; [Dependency] private readonly RoleSystem _role = default!; @@ -52,6 +54,7 @@ public override void Initialize() SubscribeLocalEvent(OnDoorjack); SubscribeLocalEvent(OnResearchStolen); SubscribeLocalEvent(OnThreatCalledIn); + SubscribeLocalEvent(OnCriminalRecordsHacked); } public override void Update(float frameTime) @@ -216,11 +219,21 @@ private void OnResearchStolen(EntityUid uid, SpaceNinjaComponent comp, ref Resea Popup.PopupEntity(str, uid, uid, PopupType.Medium); } - private void OnThreatCalledIn(EntityUid uid, SpaceNinjaComponent comp, ref ThreatCalledInEvent args) + private void OnThreatCalledIn(Entity ent, ref ThreatCalledInEvent args) { - if (_mind.TryGetObjectiveComp(uid, out var obj)) - { - obj.CalledInThreat = true; - } + _codeCondition.SetCompleted(ent.Owner, ent.Comp.TerrorObjective); + } + + private void OnCriminalRecordsHacked(Entity ent, ref CriminalRecordsHackedEvent args) + { + _codeCondition.SetCompleted(ent.Owner, ent.Comp.MassArrestObjective); + } + + /// + /// Called by when it detonates. + /// + public void DetonatedSpiderCharge(Entity ent) + { + _codeCondition.SetCompleted(ent.Owner, ent.Comp.SpiderChargeObjective); } } diff --git a/Content.Server/Ninja/Systems/SpiderChargeSystem.cs b/Content.Server/Ninja/Systems/SpiderChargeSystem.cs index 948d715f0a7..64c958d6f1a 100644 --- a/Content.Server/Ninja/Systems/SpiderChargeSystem.cs +++ b/Content.Server/Ninja/Systems/SpiderChargeSystem.cs @@ -19,6 +19,7 @@ public sealed class SpiderChargeSystem : EntitySystem [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SpaceNinjaSystem _ninja = default!; public override void Initialize() { @@ -76,10 +77,10 @@ private void OnStuck(EntityUid uid, SpiderChargeComponent comp, EntityStuckEvent /// private void OnExplode(EntityUid uid, SpiderChargeComponent comp, TriggerEvent args) { - if (comp.Planter == null || !_mind.TryGetObjectiveComp(comp.Planter.Value, out var obj)) + if (!TryComp(comp.Planter, out var ninja)) return; // assumes the target was destroyed, that the charge wasn't moved somehow - obj.Detonated = true; + _ninja.DetonatedSpiderCharge((comp.Planter.Value, ninja)); } } diff --git a/Content.Server/Objectives/Components/CodeConditionSystem.cs b/Content.Server/Objectives/Components/CodeConditionSystem.cs new file mode 100644 index 00000000000..581098c3f7e --- /dev/null +++ b/Content.Server/Objectives/Components/CodeConditionSystem.cs @@ -0,0 +1,17 @@ +using Content.Server.Objectives.Systems; + +namespace Content.Server.Objectives.Components; + +/// +/// An objective that is set to complete by code in another system. +/// Use to check and set this. +/// +[RegisterComponent, Access(typeof(CodeConditionSystem))] +public sealed partial class CodeConditionComponent : Component +{ + /// + /// Whether the objective is complete or not. + /// + [DataField] + public bool Completed; +} diff --git a/Content.Server/Objectives/Components/SpiderChargeConditionComponent.cs b/Content.Server/Objectives/Components/SpiderChargeConditionComponent.cs index 368c9f27ed4..9983b35969f 100644 --- a/Content.Server/Objectives/Components/SpiderChargeConditionComponent.cs +++ b/Content.Server/Objectives/Components/SpiderChargeConditionComponent.cs @@ -9,9 +9,6 @@ namespace Content.Server.Objectives.Components; [RegisterComponent, Access(typeof(NinjaConditionsSystem), typeof(SpiderChargeSystem), typeof(SpaceNinjaSystem))] public sealed partial class SpiderChargeConditionComponent : Component { - [DataField, ViewVariables(VVAccess.ReadWrite)] - public bool Detonated; - /// /// Warp point that the spider charge has to target /// diff --git a/Content.Server/Objectives/Components/TerrorConditionComponent.cs b/Content.Server/Objectives/Components/TerrorConditionComponent.cs deleted file mode 100644 index acd3218ad4d..00000000000 --- a/Content.Server/Objectives/Components/TerrorConditionComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Server.Objectives.Systems; -using Content.Shared.Ninja.Systems; - -namespace Content.Server.Objectives.Components; - -/// -/// Requires that the player is a ninja and has called in a threat. -/// -[RegisterComponent, Access(typeof(NinjaConditionsSystem), typeof(SharedSpaceNinjaSystem))] -public sealed partial class TerrorConditionComponent : Component -{ - /// - /// Whether the comms console has been hacked - /// - [DataField("calledInThreat"), ViewVariables(VVAccess.ReadWrite)] - public bool CalledInThreat; -} diff --git a/Content.Server/Objectives/Systems/CodeConditionSystem.cs b/Content.Server/Objectives/Systems/CodeConditionSystem.cs new file mode 100644 index 00000000000..7ba312f4bb9 --- /dev/null +++ b/Content.Server/Objectives/Systems/CodeConditionSystem.cs @@ -0,0 +1,76 @@ +using Content.Server.Objectives.Components; +using Content.Shared.Objectives.Components; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; + +namespace Content.Server.Objectives.Systems; + +/// +/// Handles progress and provides API for systems to use. +/// +public sealed class CodeConditionSystem : EntitySystem +{ + [Dependency] private readonly SharedMindSystem _mind = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetProgress); + } + + private void OnGetProgress(Entity ent, ref ObjectiveGetProgressEvent args) + { + args.Progress = ent.Comp.Completed ? 1f : 0f; + } + + /// + /// Returns whether an objective is completed. + /// + public bool IsCompleted(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + + return ent.Comp.Completed; + } + + /// + /// Returns true if a mob's objective with a certain prototype is completed. + /// + public bool IsCompleted(Entity mob, string prototype) + { + if (_mind.GetMind(mob, mob.Comp) is not {} mindId) + return false; + + if (!_mind.TryFindObjective(mindId, prototype, out var obj)) + return false; + + return IsCompleted(obj.Value); + } + + /// + /// Sets an objective's completed field. + /// + public void SetCompleted(Entity ent, bool completed = true) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + ent.Comp.Completed = completed; + } + + /// + /// Sets a mob's objective to complete. + /// + public void SetCompleted(Entity mob, string prototype, bool completed = true) + { + if (_mind.GetMind(mob, mob.Comp) is not {} mindId) + return; + + if (!_mind.TryFindObjective(mindId, prototype, out var obj)) + return; + + SetCompleted(obj.Value, completed); + } +} diff --git a/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs b/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs index 888a365a5dd..47c54b937a0 100644 --- a/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs +++ b/Content.Server/Objectives/Systems/NinjaConditionsSystem.cs @@ -23,11 +23,8 @@ public override void Initialize() SubscribeLocalEvent(OnSpiderChargeRequirementCheck); SubscribeLocalEvent(OnSpiderChargeAfterAssign); - SubscribeLocalEvent(OnSpiderChargeGetProgress); SubscribeLocalEvent(OnStealResearchGetProgress); - - SubscribeLocalEvent(OnTerrorGetProgress); } // doorjack @@ -88,11 +85,6 @@ private void OnSpiderChargeAfterAssign(EntityUid uid, SpiderChargeConditionCompo _metaData.SetEntityName(uid, title, args.Meta); } - private void OnSpiderChargeGetProgress(EntityUid uid, SpiderChargeConditionComponent comp, ref ObjectiveGetProgressEvent args) - { - args.Progress = comp.Detonated ? 1f : 0f; - } - // steal research private void OnStealResearchGetProgress(EntityUid uid, StealResearchConditionComponent comp, ref ObjectiveGetProgressEvent args) @@ -108,9 +100,4 @@ private float StealResearchProgress(StealResearchConditionComponent comp, int ta return MathF.Min(comp.DownloadedNodes.Count / (float) target, 1f); } - - private void OnTerrorGetProgress(EntityUid uid, TerrorConditionComponent comp, ref ObjectiveGetProgressEvent args) - { - args.Progress = comp.CalledInThreat ? 1f : 0f; - } } diff --git a/Content.Shared/CriminalRecords/Components/CriminalRecordsHackerComponent.cs b/Content.Shared/CriminalRecords/Components/CriminalRecordsHackerComponent.cs new file mode 100644 index 00000000000..189a387a5de --- /dev/null +++ b/Content.Shared/CriminalRecords/Components/CriminalRecordsHackerComponent.cs @@ -0,0 +1,31 @@ +using Content.Shared.CriminalRecords.Systems; +using Content.Shared.Dataset; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.CriminalRecords.Components; + +/// +/// Lets the user hack a criminal records console, once. +/// Everyone is set to wanted with a randomly picked reason. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedCriminalRecordsHackerSystem))] +public sealed partial class CriminalRecordsHackerComponent : Component +{ + /// + /// How long the doafter is for hacking it. + /// + public TimeSpan Delay = TimeSpan.FromSeconds(20); + + /// + /// Dataset of random reasons to use. + /// + [DataField] + public ProtoId Reasons = "CriminalRecordsWantedReasonPlaceholders"; + + /// + /// Announcement made after the console is hacked. + /// + [DataField] + public LocId Announcement = "ninja-criminal-records-hack-announcement"; +} diff --git a/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsHackerSystem.cs b/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsHackerSystem.cs new file mode 100644 index 00000000000..e8e8e524e24 --- /dev/null +++ b/Content.Shared/CriminalRecords/Systems/SharedCriminalRecordsHackerSystem.cs @@ -0,0 +1,48 @@ +using Content.Shared.CriminalRecords.Components; +using Content.Shared.DoAfter; +using Content.Shared.Interaction; +using Content.Shared.Ninja.Systems; +using Robust.Shared.Serialization; + +namespace Content.Shared.CriminalRecords.Systems; + +public abstract class SharedCriminalRecordsHackerSystem : EntitySystem +{ + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBeforeInteractHand); + } + + private void OnBeforeInteractHand(Entity ent, ref BeforeInteractHandEvent args) + { + // TODO: generic event + if (args.Handled || !_gloves.AbilityCheck(ent, args, out var target)) + return; + + if (!HasComp(target)) + return; + + var doAfterArgs = new DoAfterArgs(EntityManager, ent, ent.Comp.Delay, new CriminalRecordsHackDoAfterEvent(), target: target, used: ent, eventTarget: ent) + { + BreakOnDamage = true, + BreakOnUserMove = true, + MovementThreshold = 0.5f + }; + + _doAfter.TryStartDoAfter(doAfterArgs); + args.Handled = true; + } +} + +/// +/// Raised on the user when the doafter completes. +/// +[Serializable, NetSerializable] +public sealed partial class CriminalRecordsHackDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/Mind/SharedMindSystem.cs b/Content.Shared/Mind/SharedMindSystem.cs index 1898126d803..7887b8f9b22 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -383,6 +383,30 @@ public bool TryGetObjectiveComp(EntityUid mindId, [NotNullWhen(true)] out T? return false; } + /// + /// Tries to find an objective that has the same prototype as the argument. + /// + /// + /// Will not work for objectives that have no prototype, or duplicate objectives with the same prototype. + /// + public bool TryFindObjective(Entity mind, string prototype, [NotNullWhen(true)] out EntityUid? objective) + { + objective = null; + if (!Resolve(mind, ref mind.Comp)) + return false; + + foreach (var uid in mind.Comp.Objectives) + { + if (MetaData(uid).EntityPrototype?.ID == prototype) + { + objective = uid; + return true; + } + } + + return false; + } + public bool TryGetSession(EntityUid? mindId, [NotNullWhen(true)] out ICommonSession? session) { session = null; diff --git a/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs b/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs index dff4b56aa4a..0f3bff265cb 100644 --- a/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs +++ b/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs @@ -1,6 +1,6 @@ using Content.Shared.Ninja.Systems; using Robust.Shared.GameStates; -using Robust.Shared.Serialization; +using Robust.Shared.Prototypes; namespace Content.Shared.Ninja.Components; @@ -35,4 +35,22 @@ public sealed partial class SpaceNinjaComponent : Component /// [DataField("katana"), AutoNetworkedField] public EntityUid? Katana; + + /// + /// Objective to complete after calling in a threat. + /// + [DataField] + public EntProtoId TerrorObjective = "TerrorObjective"; + + /// + /// Objective to complete after setting everyone to arrest. + /// + [DataField] + public EntProtoId MassArrestObjective = "MassArrestObjective"; + + /// + /// Objective to complete after the spider charge detonates. + /// + [DataField] + public EntProtoId SpiderChargeObjective = "SpiderChargeObjective"; } diff --git a/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs b/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs index 815464bf7a3..f61d0c6a908 100644 --- a/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs @@ -1,6 +1,7 @@ using Content.Shared.Actions; using Content.Shared.CombatMode; using Content.Shared.Communications; +using Content.Shared.CriminalRecords.Components; using Content.Shared.Examine; using Content.Shared.Hands.Components; using Content.Shared.Interaction; @@ -62,6 +63,7 @@ public void DisableGloves(EntityUid uid, NinjaGlovesComponent? comp = null) RemComp(user); RemComp(user); RemComp(user); + RemComp(user); } /// diff --git a/Resources/Locale/en-US/ninja/ninja-actions.ftl b/Resources/Locale/en-US/ninja/ninja-actions.ftl index b42da33a297..f01f02a60e5 100644 --- a/Resources/Locale/en-US/ninja/ninja-actions.ftl +++ b/Resources/Locale/en-US/ninja/ninja-actions.ftl @@ -4,3 +4,5 @@ ninja-suit-cooldown = The suit needs time to recuperate from the last attack. ninja-research-steal-fail = No new research nodes were stolen... ninja-research-steal-success = Stole {$count} new nodes from {THE($server)}. + +ninja-criminal-records-hack-announcement = ERROR: Criminal records has detected a [REDACTED] error #*;" diff --git a/Resources/Prototypes/Datasets/criminal_records.yml b/Resources/Prototypes/Datasets/criminal_records.yml index ee283091843..fe21757cd20 100644 --- a/Resources/Prototypes/Datasets/criminal_records.yml +++ b/Resources/Prototypes/Datasets/criminal_records.yml @@ -2,17 +2,23 @@ - type: dataset id: CriminalRecordsWantedReasonPlaceholders values: + - Ate a delicious valid salad - Ate their own shoes - Being a clown - Being a mime - Breathed the wrong way - Broke into evac - Did literally nothing + - Did their job - Didn't say hello to me - Drank one too many + - Had two toolboxes, that's too many - Lied on common radio - Looked at me funny + - Lubed up the entire way to evac + - Set AME up on time - Slipped the HoS - Stole the clown's mask - Told an unfunny joke - Wore a gasmask + - Wore boxing gloves diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index 17c1a2b7dfc..3f1e8d4aacb 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -12,6 +12,7 @@ - DoorjackObjective - SpiderChargeObjective - TerrorObjective + - MassArrestObjective - NinjaSurviveObjective - type: NinjaRule threats: NinjaThreats diff --git a/Resources/Prototypes/Objectives/base_objectives.yml b/Resources/Prototypes/Objectives/base_objectives.yml index e24b26e6e86..2ab5149213a 100644 --- a/Resources/Prototypes/Objectives/base_objectives.yml +++ b/Resources/Prototypes/Objectives/base_objectives.yml @@ -103,3 +103,11 @@ id: BaseSurviveObjective components: - type: SurviveCondition + +# objective progress is controlled by a system and not the objective itself +- type: entity + abstract: true + parent: BaseObjective + id: BaseCodeObjective + components: + - type: CodeCondition diff --git a/Resources/Prototypes/Objectives/ninja.yml b/Resources/Prototypes/Objectives/ninja.yml index 0495be29355..fb94f2b3788 100644 --- a/Resources/Prototypes/Objectives/ninja.yml +++ b/Resources/Prototypes/Objectives/ninja.yml @@ -46,7 +46,7 @@ - type: entity noSpawn: true - parent: BaseNinjaObjective + parent: [BaseNinjaObjective, BaseCodeObjective] id: SpiderChargeObjective description: This bomb can be detonated in a specific location. Note that the bomb will not work anywhere else! components: @@ -54,7 +54,6 @@ icon: sprite: Objects/Weapons/Bombs/spidercharge.rsi state: icon - - type: SpiderChargeCondition - type: entity noSpawn: true @@ -70,7 +69,7 @@ - type: entity noSpawn: true - parent: BaseNinjaObjective + parent: [BaseNinjaObjective, BaseCodeObjective] id: TerrorObjective name: Call in a threat description: Use your gloves on a communication console in order to bring another threat to the station. @@ -79,4 +78,15 @@ icon: sprite: Objects/Fun/Instruments/otherinstruments.rsi state: red_phone - - type: TerrorCondition + +- type: entity + noSpawn: true + parent: [BaseNinjaObjective, BaseCodeObjective] + id: MassArrestObjective + name: Set everyone to wanted + description: Use your gloves to hack a criminal records console, setting the entire station to be wanted! + components: + - type: Objective + icon: + sprite: Objects/Weapons/Melee/stunbaton.rsi + state: stunbaton_on From 1fa0bad89f8dd48f101ab52e0b8b9fcf560d5b61 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Thu, 9 May 2024 06:36:07 +0000 Subject: [PATCH 057/215] malf killer 9000 (robotics console) (#24855) * create devicenet frequencies * create borg transponder and give it to all nt borgs * add robotics console * actually implement battery charge display + some fix * tab * real explosion * little safer * disable destroy button clientside too when on cooldown * m * how do i do this when i review things... Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * webedit ops Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> * ui updates * oracle java * do a thing * update ui when a borg times out * maybe fix test * add IsLocked to LockSystem * make destroying gib the chassis again, so emagging isnt sus * use locking * require using alt click to unlock so normal click is open ui * the * use LogType.Action * take this L * pocket lint? * sharer * pro ops * robor pushmarkup * m * update and make it not use prototype anymore * frame0 * update yaml * untroll * bad * h --------- Co-authored-by: deltanedas <@deltanedas:kde.org> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --- .../Robotics/Systems/RoboticsConsoleSystem.cs | 7 + .../UI/RoboticsConsoleBoundUserInterface.cs | 50 ++++++ .../Robotics/UI/RoboticsConsoleWindow.xaml | 40 +++++ .../Robotics/UI/RoboticsConsoleWindow.xaml.cs | 148 ++++++++++++++++++ .../Robotics/Systems/RoboticsConsoleSystem.cs | 146 +++++++++++++++++ .../Silicons/Borgs/BorgSystem.Transponder.cs | 107 +++++++++++++ Content.Server/Silicons/Borgs/BorgSystem.cs | 7 + Content.Shared/Lock/LockComponent.cs | 8 +- Content.Shared/Lock/LockSystem.cs | 16 +- .../Components/RoboticsConsoleComponent.cs | 53 +++++++ Content.Shared/Robotics/RoboticsConsoleUi.cs | 126 +++++++++++++++ .../Systems/SharedRoboticsConsoleSystem.cs | 8 + .../Components/BorgTransponderComponent.cs | 43 +++++ Resources/Locale/en-US/borg/borg.ftl | 5 + .../Locale/en-US/devices/device-network.ftl | 2 + .../research/components/robotics-console.ftl | 19 +++ .../Device/devicenet_frequencies.yml | 11 ++ .../Mobs/Cyborgs/base_borg_chassis.yml | 19 ++- .../Entities/Mobs/Cyborgs/borg_chassis.yml | 30 ++++ .../Devices/Circuitboards/computer.yml | 11 ++ .../Machines/Computers/computers.yml | 39 +++++ 21 files changed, 891 insertions(+), 4 deletions(-) create mode 100644 Content.Client/Robotics/Systems/RoboticsConsoleSystem.cs create mode 100644 Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs create mode 100644 Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml create mode 100644 Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs create mode 100644 Content.Server/Robotics/Systems/RoboticsConsoleSystem.cs create mode 100644 Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs create mode 100644 Content.Shared/Robotics/Components/RoboticsConsoleComponent.cs create mode 100644 Content.Shared/Robotics/RoboticsConsoleUi.cs create mode 100644 Content.Shared/Robotics/Systems/SharedRoboticsConsoleSystem.cs create mode 100644 Content.Shared/Silicons/Borgs/Components/BorgTransponderComponent.cs create mode 100644 Resources/Locale/en-US/research/components/robotics-console.ftl diff --git a/Content.Client/Robotics/Systems/RoboticsConsoleSystem.cs b/Content.Client/Robotics/Systems/RoboticsConsoleSystem.cs new file mode 100644 index 00000000000..0219c965cde --- /dev/null +++ b/Content.Client/Robotics/Systems/RoboticsConsoleSystem.cs @@ -0,0 +1,7 @@ +using Content.Shared.Robotics.Systems; + +namespace Content.Client.Robotics.Systems; + +public sealed class RoboticsConsoleSystem : SharedRoboticsConsoleSystem +{ +} diff --git a/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs b/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs new file mode 100644 index 00000000000..6185979eee6 --- /dev/null +++ b/Content.Client/Robotics/UI/RoboticsConsoleBoundUserInterface.cs @@ -0,0 +1,50 @@ +using Content.Shared.Robotics; +using Robust.Client.GameObjects; + +namespace Content.Client.Robotics.UI; + +public sealed class RoboticsConsoleBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + public RoboticsConsoleWindow _window = default!; + + public RoboticsConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _window = new RoboticsConsoleWindow(Owner); + _window.OnDisablePressed += address => + { + SendMessage(new RoboticsConsoleDisableMessage(address)); + }; + _window.OnDestroyPressed += address => + { + SendMessage(new RoboticsConsoleDestroyMessage(address)); + }; + _window.OnClose += Close; + + _window.OpenCentered(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not RoboticsConsoleState cast) + return; + + _window?.UpdateState(cast); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + _window?.Dispose(); + } +} diff --git a/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml b/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml new file mode 100644 index 00000000000..a3b39787900 --- /dev/null +++ b/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml @@ -0,0 +1,40 @@ + + + + + + + + + + private void OnEmergencyFTL(EntityUid uid, EmergencyShuttleComponent component, ref FTLStartedEvent args) { - TimeSpan ftlTime = TimeSpan.FromSeconds + var ftlTime = TimeSpan.FromSeconds ( - TryComp(uid, out var ftlComp) ? - ftlComp.TravelTime : ShuttleSystem.DefaultTravelTime + TryComp(uid, out var ftlComp) ? ftlComp.TravelTime : _shuttle.DefaultTravelTime ); if (TryComp(uid, out var netComp)) diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index a3cc2fde9b8..11cc16e0cd0 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -6,6 +6,7 @@ using Content.Server.Station.Events; using Content.Shared.Body.Components; using Content.Shared.Buckle.Components; +using Content.Shared.CCVar; using Content.Shared.Database; using Content.Shared.Ghost; using Content.Shared.Maps; @@ -34,18 +35,6 @@ public sealed partial class ShuttleSystem * This is a way to move a shuttle from one location to another, via an intermediate map for fanciness. */ - public const float DefaultStartupTime = 5.5f; - public const float DefaultTravelTime = 20f; - public const float DefaultArrivalTime = 5f; - private const float FTLCooldown = 10f; - public const float FTLMassLimit = 300f; - - // I'm too lazy to make CVars. - // >:( - // Confusingly, some of them already are cvars? - // I.e., shuttle transit time??? - // TODO Shuttle: fix spaghetti - private readonly SoundSpecifier _startupSound = new SoundPathSpecifier("/Audio/Effects/Shuttle/hyperspace_begin.ogg") { Params = AudioParams.Default.WithVolume(-5f), @@ -56,7 +45,12 @@ public sealed partial class ShuttleSystem Params = AudioParams.Default.WithVolume(-5f), }; - private readonly TimeSpan _hyperspaceKnockdownTime = TimeSpan.FromSeconds(5); + public float DefaultStartupTime; + public float DefaultTravelTime; + public float DefaultArrivalTime; + private float FTLCooldown; + public float FTLMassLimit; + private TimeSpan _hyperspaceKnockdownTime = TimeSpan.FromSeconds(5); /// /// - public HumanoidCharacterProfile() : this( - "John Doe", - "", - SharedHumanoidAppearanceSystem.DefaultSpecies, - 18, - Sex.Male, - Gender.Male, - new HumanoidCharacterAppearance(), - SpawnPriorityPreference.None, - new Dictionary - { - {SharedGameTicker.FallbackOverflowJob, JobPriority.High} - }, - PreferenceUnavailableMode.SpawnAsOverflow, - new List(), - new List(), - new Dictionary()) + public HumanoidCharacterProfile() { } @@ -149,23 +198,10 @@ public HumanoidCharacterProfile() : this( /// Humanoid character profile with default settings. public static HumanoidCharacterProfile DefaultWithSpecies(string species = SharedHumanoidAppearanceSystem.DefaultSpecies) { - return new( - "John Doe", - "", - species, - 18, - Sex.Male, - Gender.Male, - HumanoidCharacterAppearance.DefaultWithSpecies(species), - SpawnPriorityPreference.None, - new Dictionary - { - {SharedGameTicker.FallbackOverflowJob, JobPriority.High} - }, - PreferenceUnavailableMode.SpawnAsOverflow, - new List(), - new List(), - new Dictionary()); + return new() + { + Species = species, + }; } // TODO: This should eventually not be a visual change only. @@ -210,36 +246,17 @@ public static HumanoidCharacterProfile RandomWithSpecies(string species = Shared var name = GetName(species, gender); - return new HumanoidCharacterProfile(name, "", species, age, sex, gender, HumanoidCharacterAppearance.Random(species, sex), SpawnPriorityPreference.None, - new Dictionary - { - {SharedGameTicker.FallbackOverflowJob, JobPriority.High}, - }, PreferenceUnavailableMode.StayInLobby, new List(), new List(), new Dictionary()); + return new HumanoidCharacterProfile() + { + Name = name, + Sex = sex, + Age = age, + Gender = gender, + Species = species, + Appearance = HumanoidCharacterAppearance.Random(species, sex), + }; } - public string Name { get; private set; } - public string FlavorText { get; private set; } - public string Species { get; private set; } - - [DataField("age")] - public int Age { get; private set; } - - [DataField("sex")] - public Sex Sex { get; private set; } - - [DataField("gender")] - public Gender Gender { get; private set; } - - public ICharacterAppearance CharacterAppearance => Appearance; - - [DataField("appearance")] - public HumanoidCharacterAppearance Appearance { get; private set; } - public SpawnPriorityPreference SpawnPriority { get; private set; } - public IReadOnlyDictionary JobPriorities => _jobPriorities; - public IReadOnlyList AntagPreferences => _antagPreferences; - public IReadOnlyList TraitPreferences => _traitPreferences; - public PreferenceUnavailableMode PreferenceUnavailable { get; private set; } - public HumanoidCharacterProfile WithName(string name) { return new(this) { Name = name }; @@ -283,7 +300,10 @@ public HumanoidCharacterProfile WithSpawnPriorityPreference(SpawnPriorityPrefere public HumanoidCharacterProfile WithJobPriorities(IEnumerable> jobPriorities) { - return new(this, new Dictionary(jobPriorities), _antagPreferences, _traitPreferences, _loadouts); + return new(this) + { + _jobPriorities = new Dictionary(jobPriorities), + }; } public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priority) @@ -297,7 +317,11 @@ public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priori { dictionary[jobId] = priority; } - return new(this, dictionary, _antagPreferences, _traitPreferences, _loadouts); + + return new(this) + { + _jobPriorities = dictionary, + }; } public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableMode mode) @@ -307,50 +331,47 @@ public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableM public HumanoidCharacterProfile WithAntagPreferences(IEnumerable antagPreferences) { - return new(this, _jobPriorities, new List(antagPreferences), _traitPreferences, _loadouts); + return new(this) + { + _antagPreferences = new HashSet(antagPreferences), + }; } public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref) { - var list = new List(_antagPreferences); + var list = new HashSet(_antagPreferences); if (pref) { - if (!list.Contains(antagId)) - { - list.Add(antagId); - } + list.Add(antagId); } else { - if (list.Contains(antagId)) - { - list.Remove(antagId); - } + list.Remove(antagId); } - return new(this, _jobPriorities, list, _traitPreferences, _loadouts); + return new(this) + { + _antagPreferences = list, + }; } public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref) { - var list = new List(_traitPreferences); + var list = new HashSet(_traitPreferences); - // TODO: Maybe just refactor this to HashSet? Same with _antagPreferences if (pref) { - if (!list.Contains(traitId)) - { - list.Add(traitId); - } + list.Add(traitId); } else { - if (list.Contains(traitId)) - { - list.Remove(traitId); - } + list.Remove(traitId); } - return new(this, _jobPriorities, _antagPreferences, list, _loadouts); + + return new(this) + { + _traitPreferences = list, + }; } public string Summary => @@ -507,10 +528,10 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection PreferenceUnavailable = prefsUnavailableMode; _antagPreferences.Clear(); - _antagPreferences.AddRange(antags); + _antagPreferences.UnionWith(antags); _traitPreferences.Clear(); - _traitPreferences.AddRange(traits); + _traitPreferences.UnionWith(traits); // Checks prototypes exist for all loadouts and dump / set to default if not. var toRemove = new ValueList(); @@ -523,7 +544,7 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection continue; } - loadouts.EnsureValid(session, collection); + loadouts.EnsureValid(this, session, collection); } foreach (var value in toRemove) @@ -549,27 +570,26 @@ public static string GetName(string species, Gender gender) public override bool Equals(object? obj) { - return obj is HumanoidCharacterProfile other && MemberwiseEquals(other); + return ReferenceEquals(this, obj) || obj is HumanoidCharacterProfile other && Equals(other); } public override int GetHashCode() { - return HashCode.Combine( - HashCode.Combine( - Name, - Species, - Age, - Sex, - Gender, - Appearance - ), - SpawnPriority, - PreferenceUnavailable, - _jobPriorities, - _antagPreferences, - _traitPreferences, - _loadouts - ); + var hashCode = new HashCode(); + hashCode.Add(_jobPriorities); + hashCode.Add(_antagPreferences); + hashCode.Add(_traitPreferences); + hashCode.Add(_loadouts); + hashCode.Add(Name); + hashCode.Add(FlavorText); + hashCode.Add(Species); + hashCode.Add(Age); + hashCode.Add((int)Sex); + hashCode.Add((int)Gender); + hashCode.Add(Appearance); + hashCode.Add((int)SpawnPriority); + hashCode.Add((int)PreferenceUnavailable); + return hashCode.ToHashCode(); } public void SetLoadout(RoleLoadout loadout) @@ -591,10 +611,12 @@ public HumanoidCharacterProfile WithLoadout(RoleLoadout loadout) } copied[loadout.Role] = loadout.Clone(); - return new(this, _jobPriorities, _antagPreferences, _traitPreferences, copied); + var profile = Clone(); + profile._loadouts = copied; + return profile; } - public RoleLoadout GetLoadoutOrDefault(string id, IEntityManager entManager, IPrototypeManager protoManager) + public RoleLoadout GetLoadoutOrDefault(string id, ProtoId? species, IEntityManager entManager, IPrototypeManager protoManager) { if (!_loadouts.TryGetValue(id, out var loadout)) { @@ -605,5 +627,10 @@ public RoleLoadout GetLoadoutOrDefault(string id, IEntityManager entManager, IPr loadout.SetDefault(protoManager); return loadout; } + + public HumanoidCharacterProfile Clone() + { + return new HumanoidCharacterProfile(this); + } } } diff --git a/Content.Shared/Preferences/Loadouts/Effects/GroupLoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/GroupLoadoutEffect.cs index 5a2cd87cc1f..1be75f7dbc8 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/GroupLoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/GroupLoadoutEffect.cs @@ -13,13 +13,13 @@ public sealed partial class GroupLoadoutEffect : LoadoutEffect [DataField(required: true)] public ProtoId Proto; - public override bool Validate(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) + public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) { var effectsProto = collection.Resolve().Index(Proto); foreach (var effect in effectsProto.Effects) { - if (!effect.Validate(loadout, session, collection, out reason)) + if (!effect.Validate(profile, loadout, session, collection, out reason)) return false; } diff --git a/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs index 0a5d0b8b261..89d2d1312fe 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs @@ -16,7 +16,7 @@ public sealed partial class JobRequirementLoadoutEffect : LoadoutEffect [DataField(required: true)] public JobRequirement Requirement = default!; - public override bool Validate(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) + public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) { var manager = collection.Resolve(); var playtimes = manager.GetPlayTimes(session); diff --git a/Content.Shared/Preferences/Loadouts/Effects/LoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/LoadoutEffect.cs index 65694d52a18..f35b14e2e0d 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/LoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/LoadoutEffect.cs @@ -11,6 +11,7 @@ public abstract partial class LoadoutEffect /// Tries to validate the effect. /// public abstract bool Validate( + HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, diff --git a/Content.Shared/Preferences/Loadouts/Effects/PointsCostLoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/PointsCostLoadoutEffect.cs index 3146ff61636..842b4cfc036 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/PointsCostLoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/PointsCostLoadoutEffect.cs @@ -11,6 +11,7 @@ public sealed partial class PointsCostLoadoutEffect : LoadoutEffect public int Cost = 1; public override bool Validate( + HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, diff --git a/Content.Shared/Preferences/Loadouts/Effects/SpeciesLoadoutEffect.cs b/Content.Shared/Preferences/Loadouts/Effects/SpeciesLoadoutEffect.cs index 74673cbef39..8f886dd2ab8 100644 --- a/Content.Shared/Preferences/Loadouts/Effects/SpeciesLoadoutEffect.cs +++ b/Content.Shared/Preferences/Loadouts/Effects/SpeciesLoadoutEffect.cs @@ -1,6 +1,26 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Humanoid.Prototypes; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + namespace Content.Shared.Preferences.Loadouts.Effects; -public sealed class SpeciesLoadoutEffect +public sealed partial class SpeciesLoadoutEffect : LoadoutEffect { - + [DataField(required: true)] + public List> Species = new(); + + public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, + [NotNullWhen(false)] out FormattedMessage? reason) + { + if (Species.Contains(profile.Species)) + { + reason = null; + return true; + } + + reason = FormattedMessage.FromUnformatted(Loc.GetString("loadout-group-species-restriction")); + return false; + } } diff --git a/Content.Shared/Preferences/Loadouts/Loadout.cs b/Content.Shared/Preferences/Loadouts/Loadout.cs index 6a4373b6214..dbe440f58b8 100644 --- a/Content.Shared/Preferences/Loadouts/Loadout.cs +++ b/Content.Shared/Preferences/Loadouts/Loadout.cs @@ -6,8 +6,9 @@ namespace Content.Shared.Preferences.Loadouts; /// /// Specifies the selected prototype and custom data for a loadout. /// -[Serializable, NetSerializable] -public sealed class Loadout +[Serializable, NetSerializable, DataDefinition] +public sealed partial class Loadout { + [DataField] public ProtoId Prototype; } diff --git a/Content.Shared/Preferences/Loadouts/RoleLoadout.cs b/Content.Shared/Preferences/Loadouts/RoleLoadout.cs index e1c6f8395d0..40e13f0edfa 100644 --- a/Content.Shared/Preferences/Loadouts/RoleLoadout.cs +++ b/Content.Shared/Preferences/Loadouts/RoleLoadout.cs @@ -1,4 +1,6 @@ using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Shared.Humanoid.Prototypes; using Content.Shared.Random; using Robust.Shared.Collections; using Robust.Shared.Player; @@ -11,11 +13,13 @@ namespace Content.Shared.Preferences.Loadouts; /// /// Contains all of the selected data for a role's loadout. /// -[Serializable, NetSerializable] -public sealed class RoleLoadout +[Serializable, NetSerializable, DataDefinition] +public sealed partial class RoleLoadout : IEquatable { - public readonly ProtoId Role; + [DataField] + public ProtoId Role; + [DataField] public Dictionary, List> SelectedLoadouts = new(); /* @@ -44,7 +48,7 @@ public RoleLoadout Clone() /// /// Ensures all prototypes exist and effects can be applied. /// - public void EnsureValid(ICommonSession session, IDependencyCollection collection) + public void EnsureValid(HumanoidCharacterProfile profile, ICommonSession session, IDependencyCollection collection) { var groupRemove = new ValueList(); var protoManager = collection.Resolve(); @@ -81,7 +85,7 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection } // Validate the loadout can be applied (e.g. points). - if (!IsValid(session, loadout.Prototype, collection, out _)) + if (!IsValid(profile, session, loadout.Prototype, collection, out _)) { loadouts.RemoveAt(i); continue; @@ -167,7 +171,7 @@ public void SetDefault(IPrototypeManager protoManager, bool force = false) /// /// Returns whether a loadout is valid or not. /// - public bool IsValid(ICommonSession session, ProtoId loadout, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) + public bool IsValid(HumanoidCharacterProfile profile, ICommonSession session, ProtoId loadout, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason) { reason = null; @@ -180,7 +184,7 @@ public bool IsValid(ICommonSession session, ProtoId loadout, I return false; } - if (!protoManager.TryIndex(Role, out var roleProto)) + if (!protoManager.HasIndex(Role)) { reason = FormattedMessage.FromUnformatted("loadouts-prototype-missing"); return false; @@ -190,7 +194,7 @@ public bool IsValid(ICommonSession session, ProtoId loadout, I foreach (var effect in loadoutProto.Effects) { - valid = valid && effect.Validate(this, session, collection, out reason); + valid = valid && effect.Validate(profile, this, session, collection, out reason); } return valid; @@ -257,4 +261,21 @@ public bool RemoveLoadout(ProtoId selectedGroup, ProtoId< return false; } + + public bool Equals(RoleLoadout? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Role.Equals(other.Role) && SelectedLoadouts.SequenceEqual(other.SelectedLoadouts) && Points == other.Points; + } + + public override bool Equals(object? obj) + { + return ReferenceEquals(this, obj) || obj is RoleLoadout other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Role, SelectedLoadouts, Points); + } } diff --git a/Resources/Locale/en-US/preferences/ui/character-setup-gui.ftl b/Resources/Locale/en-US/preferences/ui/character-setup-gui.ftl index bd80815e231..b85d7be38ed 100644 --- a/Resources/Locale/en-US/preferences/ui/character-setup-gui.ftl +++ b/Resources/Locale/en-US/preferences/ui/character-setup-gui.ftl @@ -1,7 +1,6 @@ character-setup-gui-character-setup-label = Character setup character-setup-gui-character-setup-stats-button = Stats character-setup-gui-character-setup-rules-button = Rules -character-setup-gui-character-setup-save-button = Save character-setup-gui-character-setup-close-button = Close character-setup-gui-create-new-character-button = Create new slot... character-setup-gui-create-new-character-button-tooltip = A maximum of {$maxCharacters} characters are allowed. diff --git a/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl b/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl index 139d222f797..c7a24d54058 100644 --- a/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl +++ b/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl @@ -19,6 +19,7 @@ humanoid-profile-editor-pronouns-neuter-text = It / It humanoid-profile-editor-import-button = Import humanoid-profile-editor-export-button = Export humanoid-profile-editor-save-button = Save +humanoid-profile-editor-reset-button = Reset humanoid-profile-editor-spawn-priority-label = Spawn priority: humanoid-profile-editor-eyes-label = Eye color: humanoid-profile-editor-jobs-tab = Jobs diff --git a/Resources/Prototypes/Species/vox.yml b/Resources/Prototypes/Species/vox.yml index 53ac4258bc2..e3fdb2bf08f 100644 --- a/Resources/Prototypes/Species/vox.yml +++ b/Resources/Prototypes/Species/vox.yml @@ -1,7 +1,7 @@ - type: species id: Vox name: species-name-vox - roundStart: false # sad... + roundStart: false # sad prototype: MobVox sprites: MobVoxSprites markingLimits: MobVoxMarkingLimits From 4fbb71ffca8334d84d69612b863385b1cd3a51c5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 May 2024 23:19:28 +0000 Subject: [PATCH 125/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ae8a6fe600b..c5e4d32fa25 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: metalgearsloth - changes: - - message: Fix expedition FTL. - type: Fix - id: 6086 - time: '2024-03-04T06:24:24.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25823 - author: Nairodian changes: - message: Welding masks can now be made in the Autolathe. @@ -3864,3 +3857,16 @@ id: 6585 time: '2024-05-11T15:42:11.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27905 +- author: metalgearsloth + changes: + - message: Add a reset button to character setup. + type: Add + - message: Actually get the import / export character buttons working. + type: Add + - message: Fix performance drop in the eye color picker. + type: Fix + - message: Fix instances where role timers don't load properly in character setup. + type: Fix + id: 6586 + time: '2024-05-11T23:18:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27576 From 02aa35c47869b496c7254ece2e5aecdb00e2d4a3 Mon Sep 17 00:00:00 2001 From: Ghagliiarghii <68826635+Ghagliiarghii@users.noreply.github.com> Date: Sat, 11 May 2024 21:18:17 -0400 Subject: [PATCH 126/215] Embed a few more Cryogenics chems in Guidebook (#27935) --- Resources/ServerInfo/Guidebook/Medical/Cryogenics.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Resources/ServerInfo/Guidebook/Medical/Cryogenics.xml b/Resources/ServerInfo/Guidebook/Medical/Cryogenics.xml index f70f43c8a8b..1b3961f4f73 100644 --- a/Resources/ServerInfo/Guidebook/Medical/Cryogenics.xml +++ b/Resources/ServerInfo/Guidebook/Medical/Cryogenics.xml @@ -43,8 +43,12 @@ Once things have been set up, you're going to require a specific medication, Cry The standard pressure for a gas pump is 100.325 kpa. Cryoxadone works at under 170K, but it is standard practice to set the freezer to 100K for faster freezing. + + + + From e8dce2b0ed038e7df9f059fe734426163b1a7f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B6=D0=B5=D0=BA=D1=81=D0=BE=D0=BD=20=D0=9C=D0=B8?= =?UTF-8?q?=D1=81=D1=81=D0=B8=D1=81=D1=81=D0=B8=D0=BF=D0=BF=D0=B8?= Date: Sun, 12 May 2024 04:01:58 +0200 Subject: [PATCH 127/215] Add an admin smite for making people slip really far (and localize the admin smites better) (#27246) * Sliiiiiiiiiiiiiiiiiip * what * Localize! * antiterminate --- .../Systems/AdminVerbSystem.Smites.cs | 108 +++++++++--------- Content.Shared/Slippery/SlipperySystem.cs | 4 +- .../Locale/en-US/administration/smites.ftl | 48 +++++++- 3 files changed, 103 insertions(+), 57 deletions(-) diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index 042bac39566..a5dc6cdbb85 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -39,6 +39,7 @@ using Content.Shared.Movement.Systems; using Content.Shared.Nutrition.Components; using Content.Shared.Popups; +using Content.Shared.Slippery; using Content.Shared.Tabletop.Components; using Content.Shared.Tools.Systems; using Content.Shared.Verbs; @@ -80,6 +81,7 @@ public sealed partial class AdminVerbSystem [Dependency] private readonly SharedContentEyeSystem _eyeSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SuperBonkSystem _superBonkSystem = default!; + [Dependency] private readonly SlipperySystem _slipperySystem = default!; // All smite verbs have names so invokeverb works. private void AddSmiteVerbs(GetVerbsEvent args) @@ -98,7 +100,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb explode = new() { - Text = "Explode", + Text = "admin-smite-explode-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")), Act = () => @@ -118,7 +120,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb chess = new() { - Text = "Chess Dimension", + Text = "admin-smite-chess-dimension-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/Tabletop/chessboard.rsi"), "chessboard"), Act = () => @@ -146,7 +148,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) { Verb flames = new() { - Text = "Set Alight", + Text = "admin-smite-set-alight-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Fire/fire.png")), Act = () => @@ -168,7 +170,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb monkey = new() { - Text = "Monkeyify", + Text = "admin-smite-monkeyify-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Animals/monkey.rsi"), "monkey"), Act = () => @@ -182,7 +184,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb disposalBin = new() { - Text = "Garbage Can", + Text = "admin-smite-electrocute-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Structures/Piping/disposal.rsi"), "disposal"), Act = () => @@ -199,7 +201,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) { Verb hardElectrocute = new() { - Text = "Electrocute", + Text = "admin-smite-creampie-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Hands/Gloves/Color/yellow.rsi"), "icon"), Act = () => @@ -244,7 +246,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) { Verb creamPie = new() { - Text = "Creampie", + Text = "admin-smite-remove-blood-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Consumable/Food/Baked/pie.rsi"), "plain-slice"), Act = () => @@ -261,7 +263,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) { Verb bloodRemoval = new() { - Text = "Remove blood", + Text = "admin-smite-vomit-organs-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Fluids/tomato_splat.rsi"), "puddle-1"), Act = () => @@ -284,7 +286,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) { Verb vomitOrgans = new() { - Text = "Vomit organs", + Text = "admin-smite-remove-hands-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Fluids/vomit_toxin.rsi"), "vomit_toxin-1"), Act = () => @@ -312,7 +314,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb handsRemoval = new() { - Text = "Remove hands", + Text = "admin-smite-remove-hand-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/remove-hands.png")), Act = () => @@ -334,7 +336,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb handRemoval = new() { - Text = "Remove hand", + Text = "admin-smite-pinball-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/remove-hand.png")), Act = () => @@ -357,7 +359,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb stomachRemoval = new() { - Text = "Stomach Removal", + Text = "admin-smite-yeet-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Species/Human/organs.rsi"), "stomach"), Act = () => @@ -377,7 +379,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb lungRemoval = new() { - Text = "Lungs Removal", + Text = "admin-smite-become-bread-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Species/Human/organs.rsi"), "lung-r"), Act = () => @@ -400,7 +402,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) { Verb pinball = new() { - Text = "Pinball", + Text = "admin-smite-ghostkick-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/toys.rsi"), "basketball"), Act = () => @@ -434,7 +436,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb yeet = new() { - Text = "Yeet", + Text = "admin-smite-nyanify-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")), Act = () => @@ -465,7 +467,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb bread = new() { - Text = "Become Bread", + Text = "admin-smite-kill-sign-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Consumable/Food/Baked/bread.rsi"), "plain"), Act = () => @@ -479,7 +481,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb mouse = new() { - Text = "Become Mouse", + Text = "admin-smite-cluwne-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Animals/mouse.rsi"), "icon-0"), Act = () => @@ -495,7 +497,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) { Verb ghostKick = new() { - Text = "Ghostkick", + Text = "admin-smite-anger-pointing-arrows-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/gavel.svg.192dpi.png")), Act = () => @@ -511,7 +513,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) if (TryComp(args.Target, out var inventory)) { Verb nyanify = new() { - Text = "Nyanify", + Text = "admin-smite-dust-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Head/Hats/catears.rsi"), "icon"), Act = () => @@ -528,7 +530,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb killSign = new() { - Text = "Kill sign", + Text = "admin-smite-buffering-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Misc/killsign.rsi"), "icon"), Act = () => @@ -542,7 +544,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb cluwne = new() { - Text = "Cluwne", + Text = "admin-smite-become-instrument-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Mask/cluwne.rsi"), "icon"), @@ -558,7 +560,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb maiden = new() { - Text = "Maid", + Text = "admin-smite-remove-gravity-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Uniforms/Jumpskirt/janimaid.rsi"), "icon"), Act = () => @@ -578,7 +580,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb angerPointingArrows = new() { - Text = "Anger Pointing Arrows", + Text = "admin-smite-reptilian-species-swap-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Interface/Misc/pointing.rsi"), "pointing"), Act = () => @@ -592,7 +594,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb dust = new() { - Text = "Dust", + Text = "admin-smite-locker-stuff-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Materials/materials.rsi"), "ash"), Act = () => @@ -608,7 +610,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb youtubeVideoSimulation = new() { - Text = "Buffering", + Text = "admin-smite-headstand-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/Misc/buffering_smite_icon.png")), Act = () => @@ -622,7 +624,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb instrumentation = new() { - Text = "Become Instrument", + Text = "admin-smite-become-mouse-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/Instruments/h_synthesizer.rsi"), "icon"), Act = () => @@ -636,7 +638,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb noGravity = new() { - Text = "Remove gravity", + Text = "admin-smite-maid-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Machines/gravity_generator.rsi"), "off"), Act = () => @@ -653,7 +655,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb reptilian = new() { - Text = "Reptilian Species Swap", + Text = "admin-smite-zoom-in-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/toys.rsi"), "plushie_lizard"), Act = () => @@ -667,7 +669,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb locker = new() { - Text = "Locker stuff", + Text = "admin-smite-flip-eye-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new ("/Textures/Structures/Storage/closet.rsi"), "generic"), Act = () => @@ -689,7 +691,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb headstand = new() { - Text = "Headstand", + Text = "admin-smite-run-walk-swap-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/refresh.svg.192dpi.png")), Act = () => @@ -703,7 +705,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb zoomIn = new() { - Text = "Zoom in", + Text = "admin-smite-super-speed-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/zoom.png")), Act = () => @@ -718,7 +720,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb flipEye = new() { - Text = "Flip eye", + Text = "admin-smite-stomach-removal-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/flip.png")), Act = () => @@ -733,7 +735,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb runWalkSwap = new() { - Text = "Run Walk Swap", + Text = "admin-smite-speak-backwards-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/run-walk-swap.png")), Act = () => @@ -753,7 +755,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb backwardsAccent = new() { - Text = "Speak Backwards", + Text = "admin-smite-lung-removal-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/help-backwards.png")), Act = () => @@ -767,7 +769,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb disarmProne = new() { - Text = "Disarm Prone", + Text = "admin-smite-disarm-prone-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/Actions/disarm.png")), Act = () => @@ -781,7 +783,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Verb superSpeed = new() { - Text = "Super speed", + Text = "admin-smite-garbage-can-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/super_speed.png")), Act = () => @@ -800,7 +802,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) //Bonk Verb superBonkLite = new() { - Text = "Super Bonk Lite", + Text = "admin-smite-super-bonk-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new("Structures/Furniture/Tables/glass.rsi"), "full"), Act = () => @@ -813,7 +815,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) args.Verbs.Add(superBonkLite); Verb superBonk= new() { - Text = "Super Bonk", + Text = "admin-smite-super-bonk-lite-name", Category = VerbCategory.Smite, Icon = new SpriteSpecifier.Rsi(new("Structures/Furniture/Tables/generic.rsi"), "full"), Act = () => @@ -825,26 +827,30 @@ private void AddSmiteVerbs(GetVerbsEvent args) }; args.Verbs.Add(superBonk); - Verb terminate = new() + Verb superslip = new() { - Text = "Terminate", + Text = "admin-smite-super-slip-name", Category = VerbCategory.Smite, - Icon = new SpriteSpecifier.Rsi(new ("Mobs/Species/Terminator/parts.rsi"), "skull_icon"), + Icon = new SpriteSpecifier.Rsi(new("Objects/Specific/Janitorial/soap.rsi"), "omega-4"), Act = () => { - if (!TryComp(args.Target, out var mindContainer) || mindContainer.Mind == null) - return; - - var coords = Transform(args.Target).Coordinates; - var mindId = mindContainer.Mind.Value; - _terminator.CreateSpawner(coords, mindId); + var hadSlipComponent = EnsureComp(args.Target, out SlipperyComponent slipComponent); + if (!hadSlipComponent) + { + slipComponent.SuperSlippery = true; + slipComponent.ParalyzeTime = 5; + slipComponent.LaunchForwardsMultiplier = 20; + } - _popupSystem.PopupEntity(Loc.GetString("admin-smite-terminate-prompt"), args.Target, - args.Target, PopupType.LargeCaution); + _slipperySystem.TrySlip(args.Target, slipComponent, args.Target, requiresContact: false); + if (!hadSlipComponent) + { + RemComp(args.Target, slipComponent); + } }, Impact = LogImpact.Extreme, - Message = Loc.GetString("admin-smite-terminate-description") + Message = Loc.GetString("admin-smite-super-slip-description") }; - args.Verbs.Add(terminate); + args.Verbs.Add(superslip); } } diff --git a/Content.Shared/Slippery/SlipperySystem.cs b/Content.Shared/Slippery/SlipperySystem.cs index ff8b597a0d5..5b2a2dfe452 100644 --- a/Content.Shared/Slippery/SlipperySystem.cs +++ b/Content.Shared/Slippery/SlipperySystem.cs @@ -67,7 +67,7 @@ private bool CanSlip(EntityUid uid, EntityUid toSlip) && _statusEffects.CanApplyEffect(toSlip, "Stun"); //Should be KnockedDown instead? } - private void TrySlip(EntityUid uid, SlipperyComponent component, EntityUid other) + public void TrySlip(EntityUid uid, SlipperyComponent component, EntityUid other, bool requiresContact = true) { if (HasComp(other) && !component.SuperSlippery) return; @@ -89,7 +89,7 @@ private void TrySlip(EntityUid uid, SlipperyComponent component, EntityUid other { _physics.SetLinearVelocity(other, physics.LinearVelocity * component.LaunchForwardsMultiplier, body: physics); - if (component.SuperSlippery) + if (component.SuperSlippery && requiresContact) { var sliding = EnsureComp(other); sliding.CollidingEntities.Add(uid); diff --git a/Resources/Locale/en-US/administration/smites.ftl b/Resources/Locale/en-US/administration/smites.ftl index ae4e6f72715..9cc0e9043ce 100644 --- a/Resources/Locale/en-US/administration/smites.ftl +++ b/Resources/Locale/en-US/administration/smites.ftl @@ -15,6 +15,48 @@ admin-smite-super-speed-prompt = You move at mach 0.8! admin-smite-lung-removal-self = You can't breathe! admin-smite-terminate-prompt = I'll be back +## Smite names + +admin-smite-explode-name = Explode +admin-smite-chess-dimension-name = Chess Dimension +admin-smite-set-alight-name = Set Alight +admin-smite-monkeyify-name = Monkeyify +admin-smite-electrocute-name = Garbage Can +admin-smite-creampie-name = Electrocute +admin-smite-remove-blood-name = Creampie +admin-smite-vomit-organs-name = Remove blood +admin-smite-remove-hands-name = Vomit organs +admin-smite-remove-hand-name = Remove hands +admin-smite-pinball-name = Remove hand +admin-smite-yeet-name = Stomach Removal +admin-smite-become-bread-name = Lungs Removal +admin-smite-ghostkick-name = Pinball +admin-smite-nyanify-name = Yeet +admin-smite-kill-sign-name = Become Bread +admin-smite-cluwne-name = Become Mouse +admin-smite-anger-pointing-arrows-name = Ghostkick +admin-smite-dust-name = Nyanify +admin-smite-buffering-name = Kill sign +admin-smite-become-instrument-name = Cluwne +admin-smite-remove-gravity-name = Maid +admin-smite-reptilian-species-swap-name = Anger Pointing Arrows +admin-smite-locker-stuff-name = Dust +admin-smite-headstand-name = Buffering +admin-smite-become-mouse-name = Become Instrument +admin-smite-maid-name = Remove gravity +admin-smite-zoom-in-name = Reptilian Species Swap +admin-smite-flip-eye-name = Locker stuff +admin-smite-run-walk-swap-name = Headstand +admin-smite-super-speed-name = Zoom in +admin-smite-stomach-removal-name = Flip eye +admin-smite-speak-backwards-name = Run Walk Swap +admin-smite-lung-removal-name = Speak Backwards +admin-smite-disarm-prone-name = Disarm Prone +admin-smite-garbage-can-name = Super speed +admin-smite-super-bonk-name = Super Bonk Lite +admin-smite-super-bonk-lite-name = Super Bonk +admin-smite-terminate-name = Terminate +admin-smite-super-slip-name = Super Slip ## Smite descriptions @@ -22,12 +64,12 @@ admin-smite-explode-description = Explode them. admin-smite-chess-dimension-description = Banishment to the Chess Dimension. admin-smite-set-alight-description = Makes them burn. admin-smite-monkeyify-description = Turns the target into a monkey. -admin-smite-lung-cancer-description = Stage IIIA Lung Cancer, for when they really like the hit show Breaking Bad. admin-smite-electrocute-description = Electrocutes them, rendering anything they were wearing useless. admin-smite-creampie-description = A creampie, condensed into a button. admin-smite-remove-blood-description = Removes all of their blood, messily. admin-smite-vomit-organs-description = Causes them to vomit, organs included. admin-smite-remove-hands-description = Removes their hands. +admin-smite-remove-hand-description = Removes only one of their hands instead of all of them. admin-smite-pinball-description = Turns them into a super bouncy ball, flinging them around until they clip through the station into the abyss. admin-smite-yeet-description = Banishes them into the depths of space by turning on no-clip and tossing them. admin-smite-become-bread-description = It turns them into bread. Really, that's all it does. @@ -43,7 +85,6 @@ admin-smite-remove-gravity-description = Grants them anti-gravity. admin-smite-reptilian-species-swap-description = It changes their species to Reptilian. Useful for people who were being space racist. admin-smite-locker-stuff-description = Stuffs them in a (welded) locker. admin-smite-headstand-description = Vertically flips their sprite. -admin-smite-plasma-internals-description = Replaces the contents of their internals with plasma. admin-smite-become-mouse-description = They become a mouse. Squeak! admin-smite-maid-description = Forcibly converts them into a janitorial cat maid. This is actual torture for some players, use it wisely. admin-smite-zoom-in-description = Zooms in their view so that they can no longer see their surroundings. @@ -53,12 +94,11 @@ admin-smite-super-speed-description = Makes them really fast, causing them to tu admin-smite-stomach-removal-description = Removes their stomach, rendering them unable to eat. admin-smite-speak-backwards-description = Forces them to speak backwards, so they can't call for help. admin-smite-lung-removal-description = Removes their lungs, drowning them. -admin-smite-remove-hand-description = Removes only one of their hands instead of all of them. admin-smite-disarm-prone-description = Makes them get disarmed 100% of the time and cuffed instantly. admin-smite-garbage-can-description = Turn them into a garbage bin to emphasize what they remind you of. admin-smite-super-bonk-description = Slams them on every single table on the Station and beyond. admin-smite-super-bonk-lite-description= Slams them on every single table on the Station and beyond. Stops when the target is dead. -admin-smite-terminate-description = Creates a Terminator ghost role with the sole objective of killing them. +admin-smite-super-slip-description = Slips them really, really hard. ## Tricks descriptions From 74eef924435e1e688b70abc91df6a08ea161f7ac Mon Sep 17 00:00:00 2001 From: Kara Date: Sat, 11 May 2024 20:13:58 -0700 Subject: [PATCH 128/215] Resolve all non-obsoleting warnings in content (#27934) * Resolve all non-obsoleting warnings in content * Update ClientGameTicker.cs * Update SkeletonAccentSystem.cs * Update BwoinkSystem.cs --- .../Access/UI/IdCardConsoleBoundUserInterface.cs | 1 - .../Audio/Jukebox/JukeboxBoundUserInterface.cs | 1 - Content.Client/RCD/AlignRCDConstruction.cs | 13 ++++--------- .../Robotics/UI/RoboticsConsoleWindow.xaml.cs | 1 - Content.Client/Shuttles/UI/DockObject.xaml.cs | 4 ++++ Content.IntegrationTests/Tests/RoundEndTest.cs | 10 +++++----- .../Access/Systems/AccessOverriderSystem.cs | 1 - .../Administration/Systems/BwoinkSystem.cs | 7 +++++-- Content.Server/Anomaly/AnomalySystem.cs | 3 --- .../Atmos/EntitySystems/AirFilterSystem.cs | 1 - .../Chemistry/EntitySystems/HypospraySystem.cs | 1 - .../Unit/EntitySystems/DisposalUnitSystem.cs | 1 - Content.Server/Geras/GerasSystem.cs | 1 - Content.Server/Ghost/GhostSystem.cs | 1 - Content.Server/Ghost/Roles/GhostRoleSystem.cs | 1 - Content.Server/Ghost/Roles/UI/GhostRolesEui.cs | 2 +- Content.Server/Hands/Systems/HandsSystem.cs | 1 - Content.Server/IdentityManagement/IdentitySystem.cs | 2 +- Content.Server/Kitchen/EntitySystems/SharpSystem.cs | 7 ------- Content.Server/Ninja/Systems/NinjaGlovesSystem.cs | 1 - .../Managers/ServerPreferencesManager.cs | 1 - Content.Server/Salvage/SpawnSalvageMissionJob.cs | 2 -- .../Speech/EntitySystems/SkeletonAccentSystem.cs | 7 +++++-- Content.Server/Station/Systems/StationSystem.cs | 6 ------ .../Traitor/Uplink/Commands/AddUplinkCommand.cs | 1 - Content.Server/Wires/WiresSystem.cs | 2 +- .../Equipment/Systems/TraversalDistorterSystem.cs | 1 - .../Zombies/InitialInfectedExemptComponent.cs | 2 +- .../Access/Components/IdCardConsoleComponent.cs | 2 -- .../EntitySystems/SharedSolutionContainerSystem.cs | 6 +++--- .../EntitySystems/SolutionTransferSystem.cs | 1 - Content.Shared/Mind/SharedMindSystem.cs | 1 - Content.Shared/Pinpointer/SharedNavMapSystem.cs | 1 - Content.Shared/Plunger/Systems/PlungerSystem.cs | 4 ++-- 34 files changed, 32 insertions(+), 65 deletions(-) diff --git a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs index 5b7011c195a..a321b4121e5 100644 --- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs +++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs @@ -1,6 +1,5 @@ using Content.Shared.Access; using Content.Shared.Access.Components; -using Content.Shared.Access; using Content.Shared.Access.Systems; using Content.Shared.Containers.ItemSlots; using Content.Shared.CrewManifest; diff --git a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs index 072730d65d4..60fe339069a 100644 --- a/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs +++ b/Content.Client/Audio/Jukebox/JukeboxBoundUserInterface.cs @@ -9,7 +9,6 @@ namespace Content.Client.Audio.Jukebox; public sealed class JukeboxBoundUserInterface : BoundUserInterface { - [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; [ViewVariables] diff --git a/Content.Client/RCD/AlignRCDConstruction.cs b/Content.Client/RCD/AlignRCDConstruction.cs index da7b22c91a8..d5425425a76 100644 --- a/Content.Client/RCD/AlignRCDConstruction.cs +++ b/Content.Client/RCD/AlignRCDConstruction.cs @@ -16,9 +16,9 @@ public sealed class AlignRCDConstruction : PlacementMode { [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly SharedMapSystem _mapSystem = default!; - [Dependency] private readonly RCDSystem _rcdSystem = default!; - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + private readonly SharedMapSystem _mapSystem; + private readonly RCDSystem _rcdSystem; + private readonly SharedTransformSystem _transformSystem; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IStateManager _stateManager = default!; @@ -32,12 +32,7 @@ public sealed class AlignRCDConstruction : PlacementMode /// public AlignRCDConstruction(PlacementManager pMan) : base(pMan) { - var dependencies = IoCManager.Instance!; - _entityManager = dependencies.Resolve(); - _mapManager = dependencies.Resolve(); - _playerManager = dependencies.Resolve(); - _stateManager = dependencies.Resolve(); - + IoCManager.InjectDependencies(this); _mapSystem = _entityManager.System(); _rcdSystem = _entityManager.System(); _transformSystem = _entityManager.System(); diff --git a/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs b/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs index 3555099370e..367114f2aa6 100644 --- a/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs +++ b/Content.Client/Robotics/UI/RoboticsConsoleWindow.xaml.cs @@ -17,7 +17,6 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow { [Dependency] private readonly IEntityManager _entMan = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; private readonly LockSystem _lock; private readonly SpriteSystem _sprite; diff --git a/Content.Client/Shuttles/UI/DockObject.xaml.cs b/Content.Client/Shuttles/UI/DockObject.xaml.cs index 9dae6b7a4d3..d7d565a23e9 100644 --- a/Content.Client/Shuttles/UI/DockObject.xaml.cs +++ b/Content.Client/Shuttles/UI/DockObject.xaml.cs @@ -1,6 +1,7 @@ using System.Text; using Content.Shared.Shuttles.BUIStates; using Content.Shared.Shuttles.Systems; +using JetBrains.Annotations; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; @@ -12,7 +13,10 @@ namespace Content.Client.Shuttles.UI; [GenerateTypedNameReferences] public sealed partial class DockObject : PanelContainer { + [PublicAPI] public event Action? UndockPressed; + + [PublicAPI] public event Action? ViewPressed; public BoxContainer ContentsContainer => Contents; diff --git a/Content.IntegrationTests/Tests/RoundEndTest.cs b/Content.IntegrationTests/Tests/RoundEndTest.cs index 1ddddf66bce..69780856406 100644 --- a/Content.IntegrationTests/Tests/RoundEndTest.cs +++ b/Content.IntegrationTests/Tests/RoundEndTest.cs @@ -12,7 +12,7 @@ public sealed class RoundEndTest { private sealed class RoundEndTestSystem : EntitySystem { - public int Count; + public int RoundCount; public override void Initialize() { @@ -22,7 +22,7 @@ public override void Initialize() private void OnRoundEnd(RoundEndSystemChangedEvent ev) { - Interlocked.Increment(ref Count); + Interlocked.Increment(ref RoundCount); } } @@ -43,7 +43,7 @@ public async Task Test() var ticker = sysManager.GetEntitySystem(); var roundEndSystem = sysManager.GetEntitySystem(); var sys = server.System(); - sys.Count = 0; + sys.RoundCount = 0; await server.WaitAssertion(() => { @@ -128,8 +128,8 @@ Task CheckRunLevel(GameRunLevel level) async Task WaitForEvent() { var timeout = Task.Delay(TimeSpan.FromSeconds(10)); - var currentCount = Thread.VolatileRead(ref sys.Count); - while (currentCount == Thread.VolatileRead(ref sys.Count) && !timeout.IsCompleted) + var currentCount = Thread.VolatileRead(ref sys.RoundCount); + while (currentCount == Thread.VolatileRead(ref sys.RoundCount) && !timeout.IsCompleted) { await pair.RunTicksSync(5); } diff --git a/Content.Server/Access/Systems/AccessOverriderSystem.cs b/Content.Server/Access/Systems/AccessOverriderSystem.cs index bc038fe4ff0..c7b20513f42 100644 --- a/Content.Server/Access/Systems/AccessOverriderSystem.cs +++ b/Content.Server/Access/Systems/AccessOverriderSystem.cs @@ -28,7 +28,6 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; public override void Initialize() { diff --git a/Content.Server/Administration/Systems/BwoinkSystem.cs b/Content.Server/Administration/Systems/BwoinkSystem.cs index 2e236688ac3..a07115544bf 100644 --- a/Content.Server/Administration/Systems/BwoinkSystem.cs +++ b/Content.Server/Administration/Systems/BwoinkSystem.cs @@ -25,7 +25,7 @@ namespace Content.Server.Administration.Systems { [UsedImplicitly] - public sealed class BwoinkSystem : SharedBwoinkSystem + public sealed partial class BwoinkSystem : SharedBwoinkSystem { [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!; @@ -36,6 +36,9 @@ public sealed class BwoinkSystem : SharedBwoinkSystem [Dependency] private readonly SharedMindSystem _minds = default!; [Dependency] private readonly IAfkManager _afkManager = default!; + [GeneratedRegex(@"^https://discord\.com/api/webhooks/(\d+)/((?!.*/).*)$")] + private static partial Regex DiscordRegex(); + private ISawmill _sawmill = default!; private readonly HttpClient _httpClient = new(); private string _webhookUrl = string.Empty; @@ -157,7 +160,7 @@ private async void OnWebhookChanged(string url) return; // Basic sanity check and capturing webhook ID and token - var match = Regex.Match(url, @"^https://discord\.com/api/webhooks/(\d+)/((?!.*/).*)$"); + var match = DiscordRegex().Match(url); if (!match.Success) { diff --git a/Content.Server/Anomaly/AnomalySystem.cs b/Content.Server/Anomaly/AnomalySystem.cs index 29c83ab0299..fac50fb409b 100644 --- a/Content.Server/Anomaly/AnomalySystem.cs +++ b/Content.Server/Anomaly/AnomalySystem.cs @@ -42,9 +42,6 @@ public sealed partial class AnomalySystem : SharedAnomalySystem [Dependency] private readonly RadiationSystem _radiation = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly IComponentFactory _componentFactory = default!; - [Dependency] private readonly ISerializationManager _serialization = default!; - [Dependency] private readonly IEntityManager _entity = default!; public const float MinParticleVariation = 0.8f; public const float MaxParticleVariation = 1.2f; diff --git a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs index 2ab15cfb174..c3344c830c0 100644 --- a/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AirFilterSystem.cs @@ -12,7 +12,6 @@ namespace Content.Server.Atmos.EntitySystems; public sealed class AirFilterSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphere = default!; - [Dependency] private readonly IMapManager _map = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() diff --git a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs index dfbe45c035b..56cc0f96709 100644 --- a/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs +++ b/Content.Server/Chemistry/EntitySystems/HypospraySystem.cs @@ -25,7 +25,6 @@ public sealed class HypospraySystem : SharedHypospraySystem { [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly InteractionSystem _interaction = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; public override void Initialize() { diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index b970ae4b0df..7f4ee87ee11 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -42,7 +42,6 @@ namespace Content.Server.Disposal.Unit.EntitySystems; public sealed class DisposalUnitSystem : SharedDisposalUnitSystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; diff --git a/Content.Server/Geras/GerasSystem.cs b/Content.Server/Geras/GerasSystem.cs index e7999d64d87..f83f7c19990 100644 --- a/Content.Server/Geras/GerasSystem.cs +++ b/Content.Server/Geras/GerasSystem.cs @@ -11,7 +11,6 @@ namespace Content.Server.Geras; public sealed class GerasSystem : SharedGerasSystem { [Dependency] private readonly PolymorphSystem _polymorphSystem = default!; - [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; [Dependency] private readonly ActionsSystem _actionsSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index b1fb67cce7b..f4e6a4d607d 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -44,7 +44,6 @@ public sealed class GhostSystem : SharedGhostSystem [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; - [Dependency] private readonly IMapManager _mapManager = default!; private EntityQuery _ghostQuery; private EntityQuery _physicsQuery; diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index 5e93792d790..f8b41fd9ba4 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -30,7 +30,6 @@ using Robust.Shared.Utility; using Content.Server.Popups; using Content.Shared.Verbs; -using Robust.Shared.Prototypes; using Robust.Shared.Collections; namespace Content.Server.Ghost.Roles diff --git a/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs b/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs index fc73fc3454d..c1e39919a2f 100644 --- a/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs +++ b/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs @@ -6,7 +6,7 @@ namespace Content.Server.Ghost.Roles.UI { public sealed class GhostRolesEui : BaseEui { - [Dependency] private readonly GhostRoleSystem _ghostRoleSystem; + private readonly GhostRoleSystem _ghostRoleSystem; public GhostRolesEui() { diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 4c6b80bd987..feae130ce8e 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -33,7 +33,6 @@ public sealed class HandsSystem : SharedHandsSystem [Dependency] private readonly StackSystem _stackSystem = default!; [Dependency] private readonly VirtualItemSystem _virtualItemSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly PullingSystem _pullingSystem = default!; [Dependency] private readonly ThrowingSystem _throwingSystem = default!; diff --git a/Content.Server/IdentityManagement/IdentitySystem.cs b/Content.Server/IdentityManagement/IdentitySystem.cs index a959b96ef97..4766b89172f 100644 --- a/Content.Server/IdentityManagement/IdentitySystem.cs +++ b/Content.Server/IdentityManagement/IdentitySystem.cs @@ -19,7 +19,7 @@ namespace Content.Server.IdentityManagement; /// /// Responsible for updating the identity of an entity on init or clothing equip/unequip. /// -public class IdentitySystem : SharedIdentitySystem +public sealed class IdentitySystem : SharedIdentitySystem { [Dependency] private readonly IdCardSystem _idCard = default!; [Dependency] private readonly IAdminLogManager _adminLog = default!; diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index 3c1e89b1f2d..431e438fd8d 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -3,24 +3,17 @@ using Content.Server.Nutrition.EntitySystems; using Content.Shared.Body.Components; using Content.Shared.Administration.Logs; -using Content.Shared.Body.Components; using Content.Shared.Database; using Content.Shared.Interaction; using Content.Shared.Nutrition.Components; -using Content.Server.Nutrition.EntitySystems; using Content.Shared.Popups; using Content.Shared.Storage; using Content.Shared.Verbs; using Content.Shared.Destructible; using Content.Shared.DoAfter; -using Content.Shared.Interaction; using Content.Shared.Kitchen; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition.Components; -using Content.Shared.Popups; -using Content.Shared.Storage; -using Content.Shared.Verbs; using Robust.Server.Containers; using Robust.Shared.Random; using Robust.Shared.Utility; diff --git a/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs b/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs index 2c0f6c63e3e..ac76ae6b771 100644 --- a/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs +++ b/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs @@ -19,7 +19,6 @@ public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem [Dependency] private readonly EmagProviderSystem _emagProvider = default!; [Dependency] private readonly CodeConditionSystem _codeCondition = default!; [Dependency] private readonly CommsHackerSystem _commsHacker = default!; - [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly SharedStunProviderSystem _stunProvider = default!; [Dependency] private readonly SpaceNinjaSystem _ninja = default!; diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index 7a80757435e..1aad61715bb 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -29,7 +29,6 @@ public sealed class ServerPreferencesManager : IServerPreferencesManager [Dependency] private readonly IServerDbManager _db = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IDependencyCollection _dependencies = default!; - [Dependency] private readonly IPrototypeManager _protos = default!; // Cache player prefs on the server so we don't need as much async hell related to them. private readonly Dictionary _cachedPlayerPrefs = diff --git a/Content.Server/Salvage/SpawnSalvageMissionJob.cs b/Content.Server/Salvage/SpawnSalvageMissionJob.cs index 47123e97845..ce844e57a13 100644 --- a/Content.Server/Salvage/SpawnSalvageMissionJob.cs +++ b/Content.Server/Salvage/SpawnSalvageMissionJob.cs @@ -34,8 +34,6 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; using Content.Server.Shuttles.Components; -using Content.Shared.Coordinates; -using Content.Shared.Shuttles.Components; namespace Content.Server.Salvage; diff --git a/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs b/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs index ead9b4382e3..d143c25fdba 100644 --- a/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs +++ b/Content.Server/Speech/EntitySystems/SkeletonAccentSystem.cs @@ -4,10 +4,13 @@ namespace Content.Server.Speech.EntitySystems; -public sealed class SkeletonAccentSystem : EntitySystem +public sealed partial class SkeletonAccentSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; + [GeneratedRegex(@"(? DirectReplacements = new() { { "fuck you", "I've got a BONE to pick with you" }, @@ -45,7 +48,7 @@ public string Accentuate(string message, SkeletonAccentComponent component) // Character manipulations: // At the start of words, any non-vowel + "one" becomes "bone", e.g. tone -> bone ; lonely -> bonely; clone -> clone (remains unchanged). - msg = Regex.Replace(msg, @"(? public override void Initialize() { diff --git a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs index 79192f6b496..63be36b360a 100644 --- a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs +++ b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs @@ -12,7 +12,6 @@ namespace Content.Server.Traitor.Uplink.Commands [AdminCommand(AdminFlags.Admin)] public sealed class AddUplinkCommand : IConsoleCommand { - [Dependency] private readonly IConfigurationManager _cfgManager = default!; [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index 5d31a4d1440..c643759f504 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -348,7 +348,7 @@ public override void Update(float frameTime) } } - private class ActiveWireAction + private sealed class ActiveWireAction { /// /// The wire action's ID. This is so that once the action is finished, diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs index 2092f79f05b..d277792243d 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs @@ -11,7 +11,6 @@ namespace Content.Server.Xenoarchaeology.Equipment.Systems; public sealed class TraversalDistorterSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly PopupSystem _popup = default!; /// public override void Initialize() diff --git a/Content.Server/Zombies/InitialInfectedExemptComponent.cs b/Content.Server/Zombies/InitialInfectedExemptComponent.cs index 46077935c68..f2dfda3f872 100644 --- a/Content.Server/Zombies/InitialInfectedExemptComponent.cs +++ b/Content.Server/Zombies/InitialInfectedExemptComponent.cs @@ -1,7 +1,7 @@ namespace Content.Server.Zombies; [RegisterComponent] -public partial class InitialInfectedExemptComponent : Component +public sealed partial class InitialInfectedExemptComponent : Component { } diff --git a/Content.Shared/Access/Components/IdCardConsoleComponent.cs b/Content.Shared/Access/Components/IdCardConsoleComponent.cs index 417b77855cc..07c158021eb 100644 --- a/Content.Shared/Access/Components/IdCardConsoleComponent.cs +++ b/Content.Shared/Access/Components/IdCardConsoleComponent.cs @@ -3,8 +3,6 @@ using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; -using Robust.Shared.Prototypes; namespace Content.Shared.Access.Components; diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index fdb2f550f96..5e58a0944a4 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -60,7 +60,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem [Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!; [Dependency] protected readonly SharedHandsSystem Hands = default!; [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!; - [Dependency] protected readonly MetaDataSystem MetaData = default!; + [Dependency] protected readonly MetaDataSystem MetaDataSys = default!; [Dependency] protected readonly INetManager NetManager = default!; public override void Initialize() @@ -1123,7 +1123,7 @@ private Solution EnsureSolutionPrototype(Entity SpawnSolutionUninitialized(ContainerSlot container, string name, FixedPoint2 maxVol, Solution prototype) { var coords = new EntityCoordinates(container.Owner, Vector2.Zero); @@ -1135,7 +1135,7 @@ private Entity SpawnSolutionUnini var relation = new ContainedSolutionComponent() { Container = container.Owner, ContainerName = name }; AddComp(uid, relation); - MetaData.SetEntityName(uid, $"solution - {name}"); + MetaDataSys.SetEntityName(uid, $"solution - {name}"); ContainerSystem.Insert(uid, container, force: true); return (uid, solution, relation); diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs index 3bea79d3451..b12778262c8 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs @@ -17,7 +17,6 @@ namespace Content.Shared.Chemistry.EntitySystems; /// public sealed class SolutionTransferSystem : EntitySystem { - [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; diff --git a/Content.Shared/Mind/SharedMindSystem.cs b/Content.Shared/Mind/SharedMindSystem.cs index 7887b8f9b22..bf1065c1b1d 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -26,7 +26,6 @@ public abstract class SharedMindSystem : EntitySystem [Dependency] private readonly SharedObjectivesSystem _objectives = default!; [Dependency] private readonly SharedPlayerSystem _player = default!; [Dependency] private readonly MetaDataSystem _metadata = default!; - [Dependency] private readonly ISharedPlayerManager _playerMan = default!; [ViewVariables] protected readonly Dictionary UserMinds = new(); diff --git a/Content.Shared/Pinpointer/SharedNavMapSystem.cs b/Content.Shared/Pinpointer/SharedNavMapSystem.cs index 0edcd5a4378..ffe81c2d0ea 100644 --- a/Content.Shared/Pinpointer/SharedNavMapSystem.cs +++ b/Content.Shared/Pinpointer/SharedNavMapSystem.cs @@ -23,7 +23,6 @@ public abstract class SharedNavMapSystem : EntitySystem public const int FloorMask = AllDirMask << (int) NavMapChunkType.Floor; [Robust.Shared.IoC.Dependency] private readonly TagSystem _tagSystem = default!; - [Robust.Shared.IoC.Dependency] private readonly IGameTiming _gameTiming = default!; private readonly string[] _wallTags = ["Wall", "Window"]; private EntityQuery _doorQuery; diff --git a/Content.Shared/Plunger/Systems/PlungerSystem.cs b/Content.Shared/Plunger/Systems/PlungerSystem.cs index 57bd77a7d95..fbefad9f6e7 100644 --- a/Content.Shared/Plunger/Systems/PlungerSystem.cs +++ b/Content.Shared/Plunger/Systems/PlungerSystem.cs @@ -17,8 +17,8 @@ namespace Content.Shared.Plunger.Systems; /// public sealed class PlungerSystem : EntitySystem { - [Dependency] protected readonly IPrototypeManager _proto = default!; - [Dependency] protected readonly IRobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; From d82acccaa8a25574aaf3c46a24515b95c62b2029 Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Sun, 12 May 2024 07:30:17 -0700 Subject: [PATCH 129/215] Replace AttachToGridOrMap with DropNextTo (#27950) --- .../EntitySystems/SliceableFoodSystem.cs | 22 ++++--------------- .../Body/Systems/SharedBodySystem.Body.cs | 6 +++-- .../EntitySystems/SharedHandsSystem.Drop.cs | 11 +++------- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs index 3409a6f9a62..e872a491e3d 100644 --- a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs @@ -111,15 +111,8 @@ public EntityUid Slice(EntityUid uid, EntityUid user, SliceableFoodComponent? co // try putting the slice into the container if the food being sliced is in a container! // this lets you do things like slice a pizza up inside of a hot food cart without making a food-everywhere mess - if (_containerSystem.TryGetContainingContainer(uid, out var container) && _containerSystem.CanInsert(sliceUid, container)) - { - _containerSystem.Insert(sliceUid, container); - } - else // puts it down "right-side up" - { - _xformSystem.AttachToGridOrMap(sliceUid); - _xformSystem.SetLocalRotation(sliceUid, 0); - } + _xformSystem.DropNextTo(sliceUid, (uid, transform)); + _xformSystem.SetLocalRotation(sliceUid, 0); // DeltaV - Begin deep frier related code var sliceEvent = new SliceFoodEvent(user, uid, sliceUid); @@ -149,15 +142,8 @@ private void DeleteFood(EntityUid uid, EntityUid user, FoodComponent foodComp) var trashUid = Spawn(foodComp.Trash, _xformSystem.GetMapCoordinates(uid)); // try putting the trash in the food's container too, to be consistent with slice spawning? - if (_containerSystem.TryGetContainingContainer(uid, out var container) && _containerSystem.CanInsert(trashUid, container)) - { - _containerSystem.Insert(trashUid, container); - } - else // puts it down "right-side up" - { - _xformSystem.AttachToGridOrMap(trashUid); - _xformSystem.SetLocalRotation(trashUid, 0); - } + _xformSystem.DropNextTo(trashUid, uid); + _xformSystem.SetLocalRotation(trashUid, 0); QueueDel(uid); } diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index 1a35afdbe00..250f90db8f3 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -322,15 +322,17 @@ public virtual HashSet GibBody( launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone); } } + + var bodyTransform = Transform(bodyId); if (TryComp(bodyId, out var inventory)) { foreach (var item in _inventory.GetHandOrInventoryEntities(bodyId)) { - SharedTransform.AttachToGridOrMap(item); + SharedTransform.DropNextTo(item, (bodyId, bodyTransform)); gibs.Add(item); } } - _audioSystem.PlayPredicted(gibSoundOverride, Transform(bodyId).Coordinates, null); + _audioSystem.PlayPredicted(gibSoundOverride, bodyTransform.Coordinates, null); return gibs; } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs index 7b169b5d0a6..4d21e40a987 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs @@ -120,17 +120,12 @@ public bool TryDrop(EntityUid uid, Hand hand, EntityCoordinates? targetDropLocat return true; var userXform = Transform(uid); - var isInContainer = ContainerSystem.IsEntityInContainer(uid); + var isInContainer = ContainerSystem.IsEntityOrParentInContainer(uid, xform: userXform); if (targetDropLocation == null || isInContainer) { - // If user is in a container, drop item into that container. Otherwise, attach to grid or map.\ - // TODO recursively check upwards for containers - - if (!isInContainer - || !ContainerSystem.TryGetContainingContainer(userXform.ParentUid, uid, out var container, skipExistCheck: true) - || !ContainerSystem.Insert((entity, itemXform), container)) - TransformSystem.AttachToGridOrMap(entity, itemXform); + // If user is in a container, drop item into that container. Otherwise, attach to grid or map. + TransformSystem.DropNextTo((entity, itemXform), (uid, userXform)); return true; } From c56db3b1a8611c3f1d59d64351b682c9af5c56d2 Mon Sep 17 00:00:00 2001 From: Kara Date: Sun, 12 May 2024 16:31:54 +0200 Subject: [PATCH 130/215] Resolve `'TransformComponent.MapPosition' is obsolete` in content (#27939) * Resolve `'TransformComponent.MapPosition' is obsolete: 'Use TransformSystem.GetMapCoordinates'` in content * build? --- .../UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs | 6 ++++-- Content.Client/GPS/UI/HandheldGpsStatusControl.cs | 5 ++++- Content.Client/MouseRotator/MouseRotatorSystem.cs | 3 ++- .../Radiation/Overlays/RadiationPulseOverlay.cs | 9 +++++++-- Content.Client/Sprite/SpriteFadeSystem.cs | 4 +++- Content.Client/Tabletop/TabletopSystem.cs | 5 +++-- .../UserInterface/Systems/Chat/ChatUIController.cs | 5 +++-- Content.Client/Verbs/VerbSystem.cs | 5 +++-- Content.Client/Weapons/Melee/MeleeArcOverlay.cs | 6 ++++-- Content.Client/Weapons/Melee/MeleeSpreadCommand.cs | 3 ++- Content.Client/Weapons/Melee/MeleeWeaponSystem.cs | 2 +- Content.Client/Weapons/Ranged/GunSpreadOverlay.cs | 6 ++++-- Content.Client/Weapons/Ranged/Systems/GunSystem.cs | 3 ++- Content.IntegrationTests/Tests/Doors/AirlockTest.cs | 2 +- Content.IntegrationTests/Tests/Hands/HandTests.cs | 4 +++- .../Tests/Interaction/InRangeUnobstructed.cs | 4 +++- .../Administration/Commands/ExplosionCommand.cs | 3 ++- .../Administration/Systems/AdminVerbSystem.Smites.cs | 2 +- .../Anomaly/Effects/ElectricityAnomalySystem.cs | 2 +- .../Anomaly/Effects/InjectionAnomalySystem.cs | 4 +++- Content.Server/Beam/BeamSystem.cs | 6 ++++-- .../Chemistry/ReactionEffects/AreaReactionEffect.cs | 10 +++++++--- .../ReactionEffects/CreateEntityReactionEffect.cs | 2 +- .../Chemistry/ReactionEffects/EmpReactionEffect.cs | 6 ++++-- Content.Server/Cloning/CloningSystem.cs | 4 ++-- .../Construction/ConstructionSystem.Initial.cs | 2 +- .../Thresholds/Behaviors/SpawnEntitiesBehavior.cs | 7 +++++-- Content.Server/Disposal/Tube/DisposalTubeSystem.cs | 3 ++- Content.Server/Dragon/DragonSystem.cs | 2 +- Content.Server/Emp/EmpSystem.cs | 4 +++- .../Explosion/EntitySystems/SmokeOnTriggerSystem.cs | 7 +++++-- Content.Server/Fluids/EntitySystems/DrainSystem.cs | 4 +++- .../GameTicking/Rules/DeathMatchRuleSystem.cs | 4 +++- Content.Server/Gatherable/GatherableSystem.cs | 4 +++- Content.Server/Guardian/GuardianSystem.cs | 2 +- Content.Server/Kitchen/EntitySystems/SharpSystem.cs | 4 +++- Content.Server/Lightning/LightningSystem.cs | 4 +++- Content.Server/Lightning/LightningTargetSystem.cs | 4 +++- Content.Server/NPC/Systems/NPCSteeringSystem.cs | 2 +- Content.Server/NPC/Systems/NPCUtilitySystem.cs | 2 +- Content.Server/Nuke/NukeSystem.cs | 2 +- Content.Server/Nutrition/EntitySystems/FoodSystem.cs | 6 ++++-- Content.Server/PDA/Ringer/RingerSystem.cs | 3 ++- Content.Server/Payload/EntitySystems/PayloadSystem.cs | 4 +++- .../Pointing/EntitySystems/PointingSystem.cs | 2 +- Content.Server/Respawn/SpecialRespawnSystem.cs | 2 +- .../Singularity/EntitySystems/EventHorizonSystem.cs | 4 ++-- .../XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs | 6 ++++-- .../Effects/Systems/SpawnArtifactSystem.cs | 2 +- .../Effects/Systems/ThrowArtifactSystem.cs | 4 +++- Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs | 3 ++- Content.Shared/Examine/ExamineSystemShared.cs | 8 ++++---- .../Hands/EntitySystems/SharedHandsSystem.Pickup.cs | 4 ++-- Content.Shared/Interaction/RotateToFaceSystem.cs | 2 +- Content.Shared/Interaction/SharedInteractionSystem.cs | 6 +++--- .../Storage/EntitySystems/MagnetPickupSystem.cs | 2 +- Content.Shared/Tabletop/SharedTabletopSystem.cs | 6 +++--- Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs | 2 +- .../Ranged/Systems/SharedGunSystem.Ballistic.cs | 4 ++-- 59 files changed, 153 insertions(+), 85 deletions(-) diff --git a/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs b/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs index 5f187cad794..b0d8a946ec5 100644 --- a/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs +++ b/Content.Client/Administration/UI/SpawnExplosion/SpawnExplosionWindow.xaml.cs @@ -3,6 +3,7 @@ using JetBrains.Annotations; using Robust.Client.AutoGenerated; using Robust.Client.Console; +using Robust.Client.GameObjects; using Robust.Client.Player; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; @@ -22,7 +23,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IEntityManager _entMan = default!; - + private readonly SharedTransformSystem _transform = default!; private readonly SpawnExplosionEui _eui; private List _mapData = new(); @@ -37,6 +38,7 @@ public SpawnExplosionWindow(SpawnExplosionEui eui) { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); + _transform = _entMan.System(); _eui = eui; ExplosionOption.OnItemSelected += ExplosionSelected; @@ -104,7 +106,7 @@ private void SetLocation() _pausePreview = true; MapOptions.Select(_mapData.IndexOf(transform.MapID)); - (MapX.Value, MapY.Value) = transform.MapPosition.Position; + (MapX.Value, MapY.Value) = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: transform).Position; _pausePreview = false; UpdatePreview(); diff --git a/Content.Client/GPS/UI/HandheldGpsStatusControl.cs b/Content.Client/GPS/UI/HandheldGpsStatusControl.cs index de6a1031bad..7dcf3f29c51 100644 --- a/Content.Client/GPS/UI/HandheldGpsStatusControl.cs +++ b/Content.Client/GPS/UI/HandheldGpsStatusControl.cs @@ -1,6 +1,7 @@ using Content.Client.GPS.Components; using Content.Client.Message; using Content.Client.Stylesheets; +using Robust.Client.GameObjects; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; @@ -13,11 +14,13 @@ public sealed class HandheldGpsStatusControl : Control private readonly RichTextLabel _label; private float _updateDif; private readonly IEntityManager _entMan; + private readonly SharedTransformSystem _transform; public HandheldGpsStatusControl(Entity parent) { _parent = parent; _entMan = IoCManager.Resolve(); + _transform = _entMan.System(); _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } }; AddChild(_label); UpdateGpsDetails(); @@ -41,7 +44,7 @@ private void UpdateGpsDetails() var posText = "Error"; if (_entMan.TryGetComponent(_parent, out TransformComponent? transComp)) { - var pos = transComp.MapPosition; + var pos = _transform.GetMapCoordinates(_parent.Owner, xform: transComp); var x = (int) pos.X; var y = (int) pos.Y; posText = $"({x}, {y})"; diff --git a/Content.Client/MouseRotator/MouseRotatorSystem.cs b/Content.Client/MouseRotator/MouseRotatorSystem.cs index 4894c17c4c0..ce174c6144c 100644 --- a/Content.Client/MouseRotator/MouseRotatorSystem.cs +++ b/Content.Client/MouseRotator/MouseRotatorSystem.cs @@ -2,6 +2,7 @@ using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Client.Player; +using Robust.Client.Replays.Loading; using Robust.Shared.Map; using Robust.Shared.Timing; @@ -37,7 +38,7 @@ public override void Update(float frameTime) if (mapPos.MapId == MapId.Nullspace) return; - var angle = (mapPos.Position - xform.MapPosition.Position).ToWorldAngle(); + var angle = (mapPos.Position - _transform.GetMapCoordinates(player.Value, xform: xform).Position).ToWorldAngle(); var curRot = _transform.GetWorldRotation(xform); diff --git a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs index 9012767ef3f..8d5607af2d0 100644 --- a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs +++ b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs @@ -1,9 +1,11 @@ using System.Numerics; using Content.Shared.Radiation.Components; +using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Enums; using Robust.Shared.Graphics; using Robust.Shared.Map; +using Robust.Shared.Physics; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -14,6 +16,7 @@ public sealed class RadiationPulseOverlay : Overlay [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + private TransformSystem? _transform; private const float MaxDist = 15.0f; @@ -72,6 +75,8 @@ protected override void Draw(in OverlayDrawArgs args) //Queries all pulses on the map and either adds or removes them from the list of rendered pulses based on whether they should be drawn (in range? on the same z-level/map? pulse entity still exists?) private void RadiationQuery(IEye? currentEye) { + _transform ??= _entityManager.System(); + if (currentEye == null) { _pulses.Clear(); @@ -91,7 +96,7 @@ private void RadiationQuery(IEye? currentEye) ( _baseShader.Duplicate(), new RadiationShaderInstance( - _entityManager.GetComponent(pulseEntity).MapPosition, + _transform.GetMapCoordinates(pulseEntity), pulse.VisualRange, pulse.StartTime, pulse.VisualDuration @@ -109,7 +114,7 @@ private void RadiationQuery(IEye? currentEye) _entityManager.TryGetComponent(pulseEntity, out RadiationPulseComponent? pulse)) { var shaderInstance = _pulses[pulseEntity]; - shaderInstance.instance.CurrentMapCoords = _entityManager.GetComponent(pulseEntity).MapPosition; + shaderInstance.instance.CurrentMapCoords = _transform.GetMapCoordinates(pulseEntity); shaderInstance.instance.Range = pulse.VisualRange; } else { _pulses[pulseEntity].shd.Dispose(); diff --git a/Content.Client/Sprite/SpriteFadeSystem.cs b/Content.Client/Sprite/SpriteFadeSystem.cs index dda3a6c948e..d9584b60a65 100644 --- a/Content.Client/Sprite/SpriteFadeSystem.cs +++ b/Content.Client/Sprite/SpriteFadeSystem.cs @@ -3,6 +3,7 @@ using Robust.Client.GameObjects; using Robust.Client.Player; using Robust.Client.State; +using Robust.Shared.Physics; namespace Content.Client.Sprite; @@ -15,6 +16,7 @@ public sealed class SpriteFadeSystem : EntitySystem [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IStateManager _stateManager = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; private readonly HashSet _comps = new(); @@ -48,7 +50,7 @@ _stateManager.CurrentState is GameplayState state && spriteQuery.TryGetComponent(player, out var playerSprite)) { var fadeQuery = GetEntityQuery(); - var mapPos = playerXform.MapPosition; + var mapPos = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: playerXform); // Also want to handle large entities even if they may not be clickable. foreach (var ent in state.GetClickableEntities(mapPos)) diff --git a/Content.Client/Tabletop/TabletopSystem.cs b/Content.Client/Tabletop/TabletopSystem.cs index 696c1455e0c..0b55a1839c0 100644 --- a/Content.Client/Tabletop/TabletopSystem.cs +++ b/Content.Client/Tabletop/TabletopSystem.cs @@ -258,7 +258,7 @@ private void StopDragging(bool broadcast = true) // Set the dragging player on the component to noone if (broadcast && _draggedEntity != null && EntityManager.HasComponent(_draggedEntity.Value)) { - RaisePredictiveEvent(new TabletopMoveEvent(GetNetEntity(_draggedEntity.Value), Transform(_draggedEntity.Value).MapPosition, GetNetEntity(_table!.Value))); + RaisePredictiveEvent(new TabletopMoveEvent(GetNetEntity(_draggedEntity.Value), Transforms.GetMapCoordinates(_draggedEntity.Value), GetNetEntity(_table!.Value))); RaisePredictiveEvent(new TabletopDraggingPlayerChangedEvent(GetNetEntity(_draggedEntity.Value), false)); } @@ -277,7 +277,8 @@ private static MapCoordinates ClampPositionToViewport(MapCoordinates coordinates if (coordinates == MapCoordinates.Nullspace) return MapCoordinates.Nullspace; var eye = viewport.Eye; - if (eye == null) return MapCoordinates.Nullspace; + if (eye == null) + return MapCoordinates.Nullspace; var size = (Vector2) viewport.ViewportSize / EyeManager.PixelsPerMeter; // Convert to tiles instead of pixels var eyePosition = eye.Position.Position; diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs index 74f1172e3f7..b6ff2eb4dff 100644 --- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs +++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs @@ -62,6 +62,7 @@ public sealed class ChatUIController : UIController [UISystemDependency] private readonly TypingIndicatorSystem? _typingIndicator = default; [UISystemDependency] private readonly ChatSystem? _chatSys = default; [UISystemDependency] private readonly PsionicChatUpdateSystem? _psionic = default!; //Nyano - Summary: makes the psionic chat available. + [UISystemDependency] private readonly TransformSystem? _transform = default; [ValidatePrototypeId] private const string ChatNamePalette = "ChatNames"; @@ -639,7 +640,7 @@ private void UpdateQueuedSpeechBubbles(FrameEventArgs delta) var predicate = static (EntityUid uid, (EntityUid compOwner, EntityUid? attachedEntity) data) => uid == data.compOwner || uid == data.attachedEntity; var playerPos = player != null - ? EntityManager.GetComponent(player.Value).MapPosition + ? _transform?.GetMapCoordinates(player.Value) ?? MapCoordinates.Nullspace : MapCoordinates.Nullspace; var occluded = player != null && _examine.IsOccluded(player.Value); @@ -658,7 +659,7 @@ private void UpdateQueuedSpeechBubbles(FrameEventArgs delta) continue; } - var otherPos = EntityManager.GetComponent(ent).MapPosition; + var otherPos = _transform?.GetMapCoordinates(ent) ?? MapCoordinates.Nullspace; if (occluded && !_examine.InRangeUnOccluded( playerPos, diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 49a3785eb28..2e6c5f58099 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -22,6 +22,7 @@ public sealed class VerbSystem : SharedVerbSystem [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly ExamineSystem _examine = default!; [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; @@ -141,7 +142,7 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true if ((visibility & MenuVisibility.NoFov) == 0) { var xformQuery = GetEntityQuery(); - var playerPos = xformQuery.GetComponent(player.Value).MapPosition; + var playerPos = _transform.GetMapCoordinates(player.Value, xform: xformQuery.GetComponent(player.Value)); for (var i = entities.Count - 1; i >= 0; i--) { @@ -149,7 +150,7 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true if (!_examine.InRangeUnOccluded( playerPos, - xformQuery.GetComponent(entity).MapPosition, + _transform.GetMapCoordinates(entity, xform: xformQuery.GetComponent(entity)), ExamineSystemShared.ExamineRange, null)) { diff --git a/Content.Client/Weapons/Melee/MeleeArcOverlay.cs b/Content.Client/Weapons/Melee/MeleeArcOverlay.cs index dbd68c15e24..e7b6c8b0d60 100644 --- a/Content.Client/Weapons/Melee/MeleeArcOverlay.cs +++ b/Content.Client/Weapons/Melee/MeleeArcOverlay.cs @@ -20,10 +20,11 @@ public sealed class MeleeArcOverlay : Overlay private readonly IPlayerManager _playerManager; private readonly MeleeWeaponSystem _melee; private readonly SharedCombatModeSystem _combatMode; + private readonly SharedTransformSystem _transform = default!; public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; - public MeleeArcOverlay(IEntityManager entManager, IEyeManager eyeManager, IInputManager inputManager, IPlayerManager playerManager, MeleeWeaponSystem melee, SharedCombatModeSystem combatMode) + public MeleeArcOverlay(IEntityManager entManager, IEyeManager eyeManager, IInputManager inputManager, IPlayerManager playerManager, MeleeWeaponSystem melee, SharedCombatModeSystem combatMode, SharedTransformSystem transform) { _entManager = entManager; _eyeManager = eyeManager; @@ -31,6 +32,7 @@ public MeleeArcOverlay(IEntityManager entManager, IEyeManager eyeManager, IInput _playerManager = playerManager; _melee = melee; _combatMode = combatMode; + _transform = transform; } protected override void Draw(in OverlayDrawArgs args) @@ -52,7 +54,7 @@ protected override void Draw(in OverlayDrawArgs args) if (mapPos.MapId != args.MapId) return; - var playerPos = xform.MapPosition; + var playerPos = _transform.GetMapCoordinates(player.Value, xform: xform); if (mapPos.MapId != playerPos.MapId) return; diff --git a/Content.Client/Weapons/Melee/MeleeSpreadCommand.cs b/Content.Client/Weapons/Melee/MeleeSpreadCommand.cs index 6b259a7fd54..eda469deaf0 100644 --- a/Content.Client/Weapons/Melee/MeleeSpreadCommand.cs +++ b/Content.Client/Weapons/Melee/MeleeSpreadCommand.cs @@ -35,6 +35,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) collection.Resolve(), collection.Resolve(), sysManager.GetEntitySystem(), - sysManager.GetEntitySystem())); + sysManager.GetEntitySystem(), + sysManager.GetEntitySystem())); } } diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index 039af55bd01..d59d471f1a3 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -142,7 +142,7 @@ public override void Update(float frameTime) // Light attack if (useDown == BoundKeyState.Down) { - var attackerPos = Transform(entity).MapPosition; + var attackerPos = TransformSystem.GetMapCoordinates(entity); if (mousePos.MapId != attackerPos.MapId || (attackerPos.Position - mousePos.Position).Length() > weapon.Range) diff --git a/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs b/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs index 62df764ae50..63d21c84635 100644 --- a/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs +++ b/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs @@ -18,8 +18,9 @@ public sealed class GunSpreadOverlay : Overlay private readonly IInputManager _input; private readonly IPlayerManager _player; private readonly GunSystem _guns; + private readonly SharedTransformSystem _transform; - public GunSpreadOverlay(IEntityManager entManager, IEyeManager eyeManager, IGameTiming timing, IInputManager input, IPlayerManager player, GunSystem system) + public GunSpreadOverlay(IEntityManager entManager, IEyeManager eyeManager, IGameTiming timing, IInputManager input, IPlayerManager player, GunSystem system, SharedTransformSystem transform) { _entManager = entManager; _eye = eyeManager; @@ -27,6 +28,7 @@ public GunSpreadOverlay(IEntityManager entManager, IEyeManager eyeManager, IGame _timing = timing; _player = player; _guns = system; + _transform = transform; } protected override void Draw(in OverlayDrawArgs args) @@ -41,7 +43,7 @@ protected override void Draw(in OverlayDrawArgs args) return; } - var mapPos = xform.MapPosition; + var mapPos = _transform.GetMapCoordinates(player.Value, xform: xform); if (mapPos.MapId == MapId.Nullspace) return; diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index 4a7711032e4..ac5914d47c0 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -60,7 +60,8 @@ public bool SpreadOverlay Timing, _inputManager, _player, - this)); + this, + TransformSystem)); } else { diff --git a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs index 9f31231091f..2fbaa91456f 100644 --- a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs +++ b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs @@ -171,7 +171,7 @@ await server.WaitPost(() => // Sloth: Okay I'm sorry but I hate having to rewrite tests for every refactor // If you see this yell at me in discord so I can continue to pretend this didn't happen. // REMINDER THAT I STILL HAVE TO FIX THIS TEST EVERY OTHER PHYSICS PR - // Assert.That(AirlockPhysicsDummy.Transform.MapPosition.X, Is.GreaterThan(AirlockPhysicsDummyStartingX)); + // _transform.GetMapCoordinates(UID HERE, xform: Assert.That(AirlockPhysicsDummy.Transform).X, Is.GreaterThan(AirlockPhysicsDummyStartingX)); // Blocked by the airlock await server.WaitAssertion(() => diff --git a/Content.IntegrationTests/Tests/Hands/HandTests.cs b/Content.IntegrationTests/Tests/Hands/HandTests.cs index fdcd7f9096f..9ecabbeebf6 100644 --- a/Content.IntegrationTests/Tests/Hands/HandTests.cs +++ b/Content.IntegrationTests/Tests/Hands/HandTests.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; +using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -24,6 +25,7 @@ public async Task TestPickupDrop() var playerMan = server.ResolveDependency(); var mapMan = server.ResolveDependency(); var sys = entMan.System(); + var tSys = entMan.System(); var data = await pair.CreateTestMap(); await pair.RunTicksSync(5); @@ -35,7 +37,7 @@ await server.WaitPost(() => { player = playerMan.Sessions.First().AttachedEntity!.Value; var xform = entMan.GetComponent(player); - item = entMan.SpawnEntity("Crowbar", xform.MapPosition); + item = entMan.SpawnEntity("Crowbar", tSys.GetMapCoordinates(player, xform: xform)); hands = entMan.GetComponent(player); sys.TryPickup(player, item, hands.ActiveHand!); }); diff --git a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs index b8828763a23..719367e54e6 100644 --- a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs +++ b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Shared.Interaction; +using Robust.Server.GameObjects; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -32,6 +33,7 @@ public async Task EntityEntityTest() var sEntities = server.ResolveDependency(); var mapManager = server.ResolveDependency(); var conSystem = sEntities.EntitySysManager.GetEntitySystem(); + var tSystem = sEntities.EntitySysManager.GetEntitySystem(); EntityUid origin = default; EntityUid other = default; @@ -45,7 +47,7 @@ await server.WaitAssertion(() => origin = sEntities.SpawnEntity(HumanId, coordinates); other = sEntities.SpawnEntity(HumanId, coordinates); conSystem.EnsureContainer(other, "InRangeUnobstructedTestOtherContainer"); - mapCoordinates = sEntities.GetComponent(other).MapPosition; + mapCoordinates = tSystem.GetMapCoordinates(other); }); await server.WaitIdleAsync(); diff --git a/Content.Server/Administration/Commands/ExplosionCommand.cs b/Content.Server/Administration/Commands/ExplosionCommand.cs index 56ed78b2e2e..81daca59c48 100644 --- a/Content.Server/Administration/Commands/ExplosionCommand.cs +++ b/Content.Server/Administration/Commands/ExplosionCommand.cs @@ -8,6 +8,7 @@ using Robust.Shared.Prototypes; using System.Linq; using System.Numerics; +using Robust.Server.GameObjects; namespace Content.Server.Administration.Commands; @@ -105,7 +106,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) if (args.Length > 4) coords = new MapCoordinates(new Vector2(x, y), xform.MapID); else - coords = xform.MapPosition; + coords = entMan.System().GetMapCoordinates(shell.Player.AttachedEntity.Value, xform: xform); } ExplosionPrototype? type; diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index a5dc6cdbb85..8ff26fd72b6 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -105,7 +105,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")), Act = () => { - var coords = Transform(args.Target).MapPosition; + var coords = _transformSystem.GetMapCoordinates(args.Target); Timer.Spawn(_gameTiming.TickPeriod, () => _explosionSystem.QueueExplosion(coords, ExplosionSystem.DefaultExplosionPrototypeId, 4, 1, 2, maxTileBreak: 0), // it gibs, damage doesn't need to be high. diff --git a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs index f2a060d6295..bd4718e8e35 100644 --- a/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/ElectricityAnomalySystem.cs @@ -61,7 +61,7 @@ public override void Update(float frameTime) var damage = (int) (elec.MaxElectrocuteDamage * anom.Severity); var duration = elec.MaxElectrocuteDuration * anom.Severity; - foreach (var (ent, comp) in _lookup.GetEntitiesInRange(xform.MapPosition, range)) + foreach (var (ent, comp) in _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(uid, xform), range)) { _electrocution.TryDoElectrocution(ent, uid, damage, duration, true, statusEffects: comp, ignoreInsulation: true); } diff --git a/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs b/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs index 731d853280c..1fa0c00bd34 100644 --- a/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/InjectionAnomalySystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Anomaly.Components; using Content.Shared.Chemistry.Components.SolutionManager; using System.Linq; +using Robust.Server.GameObjects; namespace Content.Server.Anomaly.Effects; /// @@ -16,6 +17,7 @@ public sealed class InjectionAnomalySystem : EntitySystem { [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly TransformSystem _transform = default!; private EntityQuery _injectableQuery; @@ -45,7 +47,7 @@ private void PulseScalableEffect(Entity entity, float //We get all the entity in the radius into which the reagent will be injected. var xformQuery = GetEntityQuery(); var xform = xformQuery.GetComponent(entity); - var allEnts = _lookup.GetEntitiesInRange(xform.MapPosition, injectRadius) + var allEnts = _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(entity, xform: xform), injectRadius) .Select(x => x.Owner).ToList(); //for each matching entity found diff --git a/Content.Server/Beam/BeamSystem.cs b/Content.Server/Beam/BeamSystem.cs index 33f2f252d90..ad67f983c27 100644 --- a/Content.Server/Beam/BeamSystem.cs +++ b/Content.Server/Beam/BeamSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Beam; using Content.Shared.Beam.Components; using Content.Shared.Physics; +using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; @@ -16,6 +17,7 @@ namespace Content.Server.Beam; public sealed class BeamSystem : SharedBeamSystem { [Dependency] private readonly FixtureSystem _fixture = default!; + [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedBroadphaseSystem _broadphase = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; @@ -144,8 +146,8 @@ public void TryCreateBeam(EntityUid user, EntityUid target, string bodyPrototype if (Deleted(user) || Deleted(target)) return; - var userMapPos = Transform(user).MapPosition; - var targetMapPos = Transform(target).MapPosition; + var userMapPos = _transform.GetMapCoordinates(user); + var targetMapPos = _transform.GetMapCoordinates(target); //The distance between the target and the user. var calculatedDistance = targetMapPos.Position - userMapPos.Position; diff --git a/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs index 024558f8de3..ebbf4e0341b 100644 --- a/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/AreaReactionEffect.cs @@ -6,6 +6,7 @@ using Content.Shared.FixedPoint; using Content.Shared.Maps; using JetBrains.Annotations; +using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; @@ -58,15 +59,18 @@ public override void Effect(ReagentEffectArgs args) var splitSolution = args.Source.SplitSolution(args.Source.Volume); var transform = args.EntityManager.GetComponent(args.SolutionEntity); var mapManager = IoCManager.Resolve(); + var mapSys = args.EntityManager.System(); + var sys = args.EntityManager.System(); + var mapCoords = sys.GetMapCoordinates(args.SolutionEntity, xform: transform); - if (!mapManager.TryFindGridAt(transform.MapPosition, out _, out var grid) || - !grid.TryGetTileRef(transform.Coordinates, out var tileRef) || + if (!mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) || + !mapSys.TryGetTileRef(gridUid, grid, transform.Coordinates, out var tileRef) || tileRef.Tile.IsSpace()) { return; } - var coords = grid.MapToGrid(transform.MapPosition); + var coords = mapSys.MapToGrid(gridUid, mapCoords); var ent = args.EntityManager.SpawnEntity(_prototypeId, coords.SnapToGrid()); var smoke = args.EntityManager.System(); diff --git a/Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs index f8c0378452b..0d5acc17220 100644 --- a/Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/CreateEntityReactionEffect.cs @@ -34,7 +34,7 @@ public override void Effect(ReagentEffectArgs args) for (var i = 0; i < quantity; i++) { - var uid = args.EntityManager.SpawnEntity(Entity, transform.MapPosition); + var uid = args.EntityManager.SpawnEntity(Entity, transformSystem.GetMapCoordinates(args.SolutionEntity, xform: transform)); transformSystem.AttachToGridOrMap(uid); // TODO figure out how to properly spawn inside of containers diff --git a/Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs index b6714ca28d0..9a320ffc398 100644 --- a/Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/EmpReactionEffect.cs @@ -1,5 +1,6 @@ using Content.Server.Emp; using Content.Shared.Chemistry.Reagent; +using Robust.Server.GameObjects; using Robust.Shared.Prototypes; namespace Content.Server.Chemistry.ReactionEffects; @@ -37,11 +38,12 @@ public sealed partial class EmpReactionEffect : ReagentEffect public override void Effect(ReagentEffectArgs args) { + var tSys = args.EntityManager.System(); var transform = args.EntityManager.GetComponent(args.SolutionEntity); var range = MathF.Min((float) (args.Quantity*EmpRangePerUnit), EmpMaxRange); - args.EntityManager.System().EmpPulse( - transform.MapPosition, + args.EntityManager.System() + .EmpPulse(tSys.GetMapCoordinates(args.SolutionEntity, xform: transform), range, EnergyConsumption, DisableDuration); diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index 00612833676..82726650c0e 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -248,7 +248,7 @@ public bool TryCloning(EntityUid uid, EntityUid bodyToClone, Entity(mob); @@ -402,7 +402,7 @@ private EntityUid FetchAndSpawnMob(CloningPodComponent clonePod, HumanoidCharact } } - var mob = Spawn(toSpawn, Transform(clonePod.Owner).MapPosition); + var mob = Spawn(toSpawn, _transformSystem.GetMapCoordinates(clonePod.Owner)); if (TryComp(mob, out var newHumanoid)) { if (switchingSpecies || HasComp(bodyToClone)) diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index ede8d3064fa..17ed5c90f4d 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -79,7 +79,7 @@ private IEnumerable EnumerateNearby(EntityUid user) } } - var pos = Transform(user).MapPosition; + var pos = _transformSystem.GetMapCoordinates(user); foreach (var near in _lookupSystem.GetEntitiesInRange(pos, 2f, LookupFlags.Contained | LookupFlags.Dynamic | LookupFlags.Sundries | LookupFlags.Approximate)) { diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs index 0fa3c06c04b..057b6df9df2 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs @@ -3,6 +3,7 @@ using Content.Server.Stack; using Content.Shared.Prototypes; using Content.Shared.Stacks; +using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; @@ -30,7 +31,8 @@ public sealed partial class SpawnEntitiesBehavior : IThresholdBehavior public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) { - var position = system.EntityManager.GetComponent(owner).MapPosition; + var tSys = system.EntityManager.System(); + var position = tSys.GetMapCoordinates(owner); var getRandomVector = () => new Vector2(system.Random.NextFloat(-Offset, Offset), system.Random.NextFloat(-Offset, Offset)); @@ -48,7 +50,8 @@ public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause ? minMax.Min : system.Random.Next(minMax.Min, minMax.Max + 1); - if (count == 0) continue; + if (count == 0) + continue; if (EntityPrototypeHelpers.HasComponent(entityId, system.PrototypeManager, system.ComponentFactory)) { diff --git a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs index f0f6e9142c6..b38e6e78f47 100644 --- a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs @@ -31,6 +31,7 @@ public sealed class DisposalTubeSystem : EntitySystem [Dependency] private readonly DisposableSystem _disposableSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { base.Initialize(); @@ -422,7 +423,7 @@ public bool TryInsert(EntityUid uid, DisposalUnitComponent from, IEnumerable(holder); foreach (var entity in from.Container.ContainedEntities.ToArray()) diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs index d33e6f3bef7..1e29b80ebaf 100644 --- a/Content.Server/Dragon/DragonSystem.cs +++ b/Content.Server/Dragon/DragonSystem.cs @@ -165,7 +165,7 @@ private void OnSpawnRift(EntityUid uid, DragonComponent component, DragonSpawnRi return; } - var carpUid = Spawn(component.RiftPrototype, xform.MapPosition); + var carpUid = Spawn(component.RiftPrototype, _transform.GetMapCoordinates(uid, xform: xform)); component.Rifts.Add(carpUid); Comp(carpUid).Dragon = uid; } diff --git a/Content.Server/Emp/EmpSystem.cs b/Content.Server/Emp/EmpSystem.cs index 7c1a6f9b5db..d8eab0c5d1f 100644 --- a/Content.Server/Emp/EmpSystem.cs +++ b/Content.Server/Emp/EmpSystem.cs @@ -4,6 +4,7 @@ using Content.Server.SurveillanceCamera; using Content.Shared.Emp; using Content.Shared.Examine; +using Robust.Server.GameObjects; using Robust.Shared.Map; namespace Content.Server.Emp; @@ -11,6 +12,7 @@ namespace Content.Server.Emp; public sealed class EmpSystem : SharedEmpSystem { [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TransformSystem _transform = default!; public const string EmpPulseEffectPrototype = "EffectEmpPulse"; @@ -102,7 +104,7 @@ private void OnExamine(EntityUid uid, EmpDisabledComponent component, ExaminedEv private void HandleEmpTrigger(EntityUid uid, EmpOnTriggerComponent comp, TriggerEvent args) { - EmpPulse(Transform(uid).MapPosition, comp.Range, comp.EnergyConsumption, comp.DisableDuration); + EmpPulse(_transform.GetMapCoordinates(uid), comp.Range, comp.EnergyConsumption, comp.DisableDuration); args.Handled = true; } diff --git a/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs b/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs index 17ca9723569..f958373ac74 100644 --- a/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Chemistry.Components; using Content.Shared.Coordinates.Helpers; using Content.Shared.Maps; +using Robust.Server.GameObjects; using Robust.Shared.Map; namespace Content.Server.Explosion.EntitySystems; @@ -15,6 +16,7 @@ public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem { [Dependency] private readonly IMapManager _mapMan = default!; [Dependency] private readonly SmokeSystem _smoke = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { @@ -26,14 +28,15 @@ public override void Initialize() private void OnTrigger(EntityUid uid, SmokeOnTriggerComponent comp, TriggerEvent args) { var xform = Transform(uid); - if (!_mapMan.TryFindGridAt(xform.MapPosition, out _, out var grid) || + var mapCoords = _transform.GetMapCoordinates(uid, xform); + if (!_mapMan.TryFindGridAt(mapCoords, out _, out var grid) || !grid.TryGetTileRef(xform.Coordinates, out var tileRef) || tileRef.Tile.IsSpace()) { return; } - var coords = grid.MapToGrid(xform.MapPosition); + var coords = grid.MapToGrid(mapCoords); var ent = Spawn(comp.SmokePrototype, coords.SnapToGrid()); if (!TryComp(ent, out var smoke)) { diff --git a/Content.Server/Fluids/EntitySystems/DrainSystem.cs b/Content.Server/Fluids/EntitySystems/DrainSystem.cs index 5fc406dca53..b79685d83b6 100644 --- a/Content.Server/Fluids/EntitySystems/DrainSystem.cs +++ b/Content.Server/Fluids/EntitySystems/DrainSystem.cs @@ -13,6 +13,7 @@ using Content.Shared.Interaction; using Content.Shared.Tag; using Content.Shared.Verbs; +using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Collections; using Robust.Shared.Prototypes; @@ -31,6 +32,7 @@ public sealed class DrainSystem : SharedDrainSystem [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly PuddleSystem _puddleSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -161,7 +163,7 @@ public override void Update(float frameTime) puddles.Clear(); - foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, drain.Range)) + foreach (var entity in _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(uid, xform), drain.Range)) { // No InRangeUnobstructed because there's no collision group that fits right now // and these are placed by mappers and not buildable/movable so shouldnt really be a problem... diff --git a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs index 78b8a8a85c8..ad7c63ff58d 100644 --- a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs @@ -9,6 +9,7 @@ using Content.Server.Station.Systems; using Content.Shared.Points; using Content.Shared.Storage; +using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Utility; @@ -25,6 +26,7 @@ public sealed class DeathMatchRuleSystem : GameRuleSystem().ToList(); - EntityManager.SpawnEntities(Transform(ev.Entity).MapPosition, spawns); + EntityManager.SpawnEntities(_transform.GetMapCoordinates(ev.Entity), spawns); } } diff --git a/Content.Server/Gatherable/GatherableSystem.cs b/Content.Server/Gatherable/GatherableSystem.cs index 7fbbf7f4f64..11295bb3a35 100644 --- a/Content.Server/Gatherable/GatherableSystem.cs +++ b/Content.Server/Gatherable/GatherableSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Interaction; using Content.Shared.Tag; using Content.Shared.Weapons.Melee.Events; +using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; @@ -18,6 +19,7 @@ public sealed partial class GatherableSystem : EntitySystem [Dependency] private readonly DestructibleSystem _destructible = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { @@ -61,7 +63,7 @@ public void Gather(EntityUid gatheredUid, EntityUid? gatherer = null, Gatherable if (component.MappedLoot == null) return; - var pos = Transform(gatheredUid).MapPosition; + var pos = _transform.GetMapCoordinates(gatheredUid); foreach (var (tag, table) in component.MappedLoot) { diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs index 97d4eb06803..203882ed9ef 100644 --- a/Content.Server/Guardian/GuardianSystem.cs +++ b/Content.Server/Guardian/GuardianSystem.cs @@ -208,7 +208,7 @@ private void OnDoAfter(EntityUid uid, GuardianCreatorComponent component, DoAfte var hostXform = Transform(args.Args.Target.Value); var host = EnsureComp(args.Args.Target.Value); // Use map position so it's not inadvertantly parented to the host + if it's in a container it spawns outside I guess. - var guardian = Spawn(component.GuardianProto, hostXform.MapPosition); + var guardian = Spawn(component.GuardianProto, _transform.GetMapCoordinates(args.Args.Target.Value, xform: hostXform)); _container.Insert(guardian, host.GuardianContainer); host.HostedGuardian = guardian; diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index 431e438fd8d..b7966877214 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Robust.Server.Containers; +using Robust.Server.GameObjects; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -28,6 +29,7 @@ public sealed class SharpSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly ContainerSystem _containerSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; @@ -101,7 +103,7 @@ private void OnDoAfter(EntityUid uid, SharpComponent component, DoAfterEvent arg } var spawnEntities = EntitySpawnCollection.GetSpawns(butcher.SpawnedEntities, _robustRandom); - var coords = Transform(args.Args.Target.Value).MapPosition; + var coords = _transform.GetMapCoordinates(args.Args.Target.Value); EntityUid popupEnt = default!; foreach (var proto in spawnEntities) { diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 4f975a60fda..2147ac80f2e 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Beam.Components; using Content.Server.Lightning.Components; using Content.Shared.Lightning; +using Robust.Server.GameObjects; using Robust.Shared.Random; namespace Content.Server.Lightning; @@ -20,6 +21,7 @@ public sealed class LightningSystem : SharedLightningSystem [Dependency] private readonly BeamSystem _beam = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { @@ -74,7 +76,7 @@ public void ShootRandomLightnings(EntityUid user, float range, int boltCount, st //To Do: This is still pretty bad for perf but better than before and at least it doesn't re-allocate // several hashsets every time - var targets = _lookup.GetComponentsInRange(Transform(user).MapPosition, range).ToList(); + var targets = _lookup.GetComponentsInRange(_transform.GetMapCoordinates(user), range).ToList(); _random.Shuffle(targets); targets.Sort((x, y) => y.Priority.CompareTo(x.Priority)); diff --git a/Content.Server/Lightning/LightningTargetSystem.cs b/Content.Server/Lightning/LightningTargetSystem.cs index ccaa74e9e26..bc99def9742 100644 --- a/Content.Server/Lightning/LightningTargetSystem.cs +++ b/Content.Server/Lightning/LightningTargetSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Lightning; using Content.Server.Lightning.Components; using Content.Shared.Damage; +using Robust.Server.GameObjects; namespace Content.Server.Tesla.EntitySystems; @@ -12,6 +13,7 @@ public sealed class LightningTargetSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly ExplosionSystem _explosionSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; public override void Initialize() { @@ -29,7 +31,7 @@ private void OnHitByLightning(Entity uid, ref HitByLig if (uid.Comp.LightningExplode) { _explosionSystem.QueueExplosion( - Transform(uid).MapPosition, + _transform.GetMapCoordinates(uid), uid.Comp.ExplosionPrototype, uid.Comp.TotalIntensity, uid.Comp.Dropoff, uid.Comp.MaxTileIntensity, diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.cs index 153a1738555..a77af941743 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.cs @@ -454,7 +454,7 @@ private async void RequestPath(EntityUid uid, NPCSteeringComponent steering, Tra } var targetPos = steering.Coordinates.ToMap(EntityManager, _transform); - var ourPos = xform.MapPosition; + var ourPos = _transform.GetMapCoordinates(uid, xform: xform); PrunePath(uid, ourPos, targetPos.Position - ourPos.Position, result.Path); steering.CurrentPath = new Queue(result.Path); diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index e8fb54022ee..4b0ccafa1d4 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -372,7 +372,7 @@ private void Add(NPCBlackboard blackboard, HashSet entities, UtilityQ if (compQuery.Components.Count == 0) return; - var mapPos = _xformQuery.GetComponent(owner).MapPosition; + var mapPos = _transform.GetMapCoordinates(owner, xform: _xformQuery.GetComponent(owner)); _compTypes.Clear(); var i = -1; EntityPrototype.ComponentRegistryEntry compZero = default!; diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index b72be0b46c9..db425b491eb 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -450,7 +450,7 @@ public void ArmBomb(EntityUid uid, NukeComponent? component = null) if (stationUid != null) _alertLevel.SetLevel(stationUid.Value, component.AlertLevelOnActivate, true, true, true, true); - var pos = nukeXform.MapPosition; + var pos = _transform.GetMapCoordinates(uid, xform: nukeXform); var x = (int) pos.X; var y = (int) pos.Y; var posText = $"({x}, {y})"; diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 49d73740412..2c7632aadca 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -31,6 +31,7 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Utility; using System.Linq; +using Robust.Server.GameObjects; namespace Content.Server.Nutrition.EntitySystems; @@ -52,6 +53,7 @@ public sealed class FoodSystem : EntitySystem [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly StomachSystem _stomach = default!; [Dependency] private readonly UtensilSystem _utensil = default!; @@ -150,7 +152,7 @@ private void OnFeedFood(Entity entity, ref AfterInteractEvent arg return (false, true); // TODO make do-afters account for fixtures in the range check. - if (!Transform(user).MapPosition.InRange(Transform(target).MapPosition, MaxFeedDistance)) + if (!_transform.GetMapCoordinates(user).InRange(_transform.GetMapCoordinates(target), MaxFeedDistance)) { var message = Loc.GetString("interaction-system-user-interaction-cannot-reach"); _popup.PopupEntity(message, user, user); @@ -325,7 +327,7 @@ public void DeleteAndSpawnTrash(FoodComponent component, EntityUid food, EntityU } //We're empty. Become trash. - var position = Transform(food).MapPosition; + var position = _transform.GetMapCoordinates(food); var finisher = Spawn(component.Trash, position); // If the user is holding the item diff --git a/Content.Server/PDA/Ringer/RingerSystem.cs b/Content.Server/PDA/Ringer/RingerSystem.cs index a10544d6966..47ae41896e2 100644 --- a/Content.Server/PDA/Ringer/RingerSystem.cs +++ b/Content.Server/PDA/Ringer/RingerSystem.cs @@ -25,6 +25,7 @@ public sealed class RingerSystem : SharedRingerSystem [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; private readonly Dictionary _lastSetRingtoneAt = new(); @@ -210,7 +211,7 @@ public bool ToggleRingerUI(EntityUid uid, EntityUid actor) _audio.PlayEntity( GetSound(ringer.Ringtone[ringer.NoteCount]), - Filter.Empty().AddInRange(ringerXform.MapPosition, ringer.Range), + Filter.Empty().AddInRange(_transform.GetMapCoordinates(uid, ringerXform), ringer.Range), uid, true, AudioParams.Default.WithMaxDistance(ringer.Range).WithVolume(ringer.Volume) diff --git a/Content.Server/Payload/EntitySystems/PayloadSystem.cs b/Content.Server/Payload/EntitySystems/PayloadSystem.cs index 85cf303d5d7..15966956d4f 100644 --- a/Content.Server/Payload/EntitySystems/PayloadSystem.cs +++ b/Content.Server/Payload/EntitySystems/PayloadSystem.cs @@ -10,6 +10,7 @@ using Robust.Shared.Serialization.Manager; using Robust.Shared.Utility; using System.Linq; +using Robust.Server.GameObjects; namespace Content.Server.Payload.EntitySystems; @@ -17,6 +18,7 @@ public sealed class PayloadSystem : EntitySystem { [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IComponentFactory _componentFactory = default!; [Dependency] private readonly ISerializationManager _serializationManager = default!; @@ -158,7 +160,7 @@ private void HandleChemicalPayloadTrigger(Entity entit var solStringB = SolutionContainerSystem.ToPrettyString(solutionB); _adminLogger.Add(LogType.ChemicalReaction, - $"Chemical bomb payload {ToPrettyString(entity.Owner):payload} at {Transform(entity.Owner).MapPosition:location} is combining two solutions: {solStringA:solutionA} and {solStringB:solutionB}"); + $"Chemical bomb payload {ToPrettyString(entity.Owner):payload} at {_transform.GetMapCoordinates(entity.Owner):location} is combining two solutions: {solStringA:solutionA} and {solStringB:solutionB}"); solutionA.MaxVolume += solutionB.MaxVolume; _solutionContainerSystem.TryAddSolution(solnA.Value, solutionB); diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index 9b2e14eff8b..7bbf6409cdd 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -183,7 +183,7 @@ bool ViewerPredicate(ICommonSession playerSession) (eyeComp.VisibilityMask & layer) == 0) return false; - return Transform(ent).MapPosition.InRange(Transform(player).MapPosition, PointingRange); + return _transform.GetMapCoordinates(ent).InRange(_transform.GetMapCoordinates(player), PointingRange); } var viewers = Filter.Empty() diff --git a/Content.Server/Respawn/SpecialRespawnSystem.cs b/Content.Server/Respawn/SpecialRespawnSystem.cs index 2463bcd7402..6c7bb5c9234 100644 --- a/Content.Server/Respawn/SpecialRespawnSystem.cs +++ b/Content.Server/Respawn/SpecialRespawnSystem.cs @@ -95,7 +95,7 @@ private void OnTermination(EntityUid uid, SpecialRespawnComponent component, ref { var xform = Transform(entityGridUid.Value); var pos = xform.Coordinates; - var mapPos = xform.MapPosition; + var mapPos = _transform.GetMapCoordinates(entityGridUid.Value, xform: xform); var circle = new Circle(mapPos.Position, 2); var found = false; diff --git a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs index c74a3c49d63..ae8efa1de38 100644 --- a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs @@ -167,7 +167,7 @@ public void ConsumeEntitiesInRange(EntityUid uid, float range, TransformComponen var range2 = range * range; var xformQuery = EntityManager.GetEntityQuery(); var epicenter = _xformSystem.GetWorldPosition(xform, xformQuery); - foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, range, flags: LookupFlags.Uncontained)) + foreach (var entity in _lookup.GetEntitiesInRange(_xformSystem.GetMapCoordinates(uid, xform), range, flags: LookupFlags.Uncontained)) { if (entity == uid) continue; @@ -297,7 +297,7 @@ public void ConsumeTilesInRange(EntityUid uid, float range, TransformComponent? if (!Resolve(uid, ref xform) || !Resolve(uid, ref eventHorizon)) return; - var mapPos = xform.MapPosition; + var mapPos = _xformSystem.GetMapCoordinates(uid, xform: xform); var box = Box2.CenteredAround(mapPos.Position, new Vector2(range, range)); var circle = new Circle(mapPos.Position, range); var grids = new List>(); diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs index d4ed8272aa3..970743f4848 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/EmpArtifactSystem.cs @@ -1,12 +1,14 @@ using Content.Server.Emp; using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components; using Content.Server.Xenoarchaeology.XenoArtifacts.Events; +using Robust.Server.GameObjects; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems; public sealed class EmpArtifactSystem : EntitySystem { [Dependency] private readonly EmpSystem _emp = default!; + [Dependency] private readonly TransformSystem _transform = default!; /// public override void Initialize() @@ -16,6 +18,6 @@ public override void Initialize() private void OnActivate(EntityUid uid, EmpArtifactComponent component, ArtifactActivatedEvent args) { - _emp.EmpPulse(Transform(uid).MapPosition, component.Range, component.EnergyConsumption, component.DisableDuration); + _emp.EmpPulse(_transform.GetMapCoordinates(uid), component.Range, component.EnergyConsumption, component.DisableDuration); } -} \ No newline at end of file +} diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/SpawnArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/SpawnArtifactSystem.cs index fcb33ae41fd..c2622837872 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/SpawnArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/SpawnArtifactSystem.cs @@ -32,7 +32,7 @@ private void OnActivate(EntityUid uid, SpawnArtifactComponent component, Artifac if (component.Spawns is not {} spawns) return; - var artifactCord = Transform(uid).MapPosition; + var artifactCord = _transform.GetMapCoordinates(uid); foreach (var spawn in EntitySpawnCollection.GetSpawns(spawns, _random)) { var dx = _random.NextFloat(-component.Range, component.Range); diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs index 57a30a2fd9e..8708e0ff4e8 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/ThrowArtifactSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Maps; using Content.Shared.Physics; using Content.Shared.Throwing; +using Robust.Server.GameObjects; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Random; @@ -16,6 +17,7 @@ public sealed class ThrowArtifactSystem : EntitySystem [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly TileSystem _tile = default!; + [Dependency] private readonly TransformSystem _transform = default!; /// public override void Initialize() @@ -50,7 +52,7 @@ private void OnActivated(EntityUid uid, ThrowArtifactComponent component, Artifa var tempXform = Transform(ent); - var foo = tempXform.MapPosition.Position - xform.MapPosition.Position; + var foo = _transform.GetMapCoordinates(ent, xform: tempXform).Position - _transform.GetMapCoordinates(uid, xform: xform).Position; _throwing.TryThrow(ent, foo*2, component.ThrowStrength, uid, 0); } } diff --git a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs index 83aa7085897..79a32268e80 100644 --- a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs +++ b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs @@ -13,6 +13,7 @@ public abstract class SharedDeviceLinkSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; public const string InvokedPort = "link_port"; @@ -529,7 +530,7 @@ private bool CanLink( private bool InRange(EntityUid sourceUid, EntityUid sinkUid, float range) { // TODO: This should be using an existing method and also coordinates inrange instead. - return Transform(sourceUid).MapPosition.InRange(Transform(sinkUid).MapPosition, range); + return _transform.GetMapCoordinates(sourceUid).InRange(_transform.GetMapCoordinates(sinkUid), range); } private void SendNewLinkEvent(EntityUid? user, EntityUid sourceUid, string source, EntityUid sinkUid, string sink) diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 2a5201f7687..8831213c352 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -210,8 +210,8 @@ public bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates othe public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); - var originPos = entMan.GetComponent(origin).MapPosition; - var otherPos = entMan.GetComponent(other).MapPosition; + var originPos = _transform.GetMapCoordinates(origin); + var otherPos = _transform.GetMapCoordinates(other); return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker); } @@ -219,7 +219,7 @@ public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = E public bool InRangeUnOccluded(EntityUid origin, EntityCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); - var originPos = entMan.GetComponent(origin).MapPosition; + var originPos = _transform.GetMapCoordinates(origin); var otherPos = other.ToMap(entMan, _transform); return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker); @@ -228,7 +228,7 @@ public bool InRangeUnOccluded(EntityUid origin, EntityCoordinates other, float r public bool InRangeUnOccluded(EntityUid origin, MapCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); - var originPos = entMan.GetComponent(origin).MapPosition; + var originPos = _transform.GetMapCoordinates(origin); return InRangeUnOccluded(originPos, other, range, predicate, ignoreInsideBlocker); } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs index 20e08b2767d..d1f41738e9d 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs @@ -108,10 +108,10 @@ public bool TryPickup( var xform = Transform(uid); var coordinateEntity = xform.ParentUid.IsValid() ? xform.ParentUid : uid; var itemXform = Transform(entity); - var itemPos = itemXform.MapPosition; + var itemPos = TransformSystem.GetMapCoordinates(entity, xform: itemXform); if (itemPos.MapId == xform.MapID - && (itemPos.Position - xform.MapPosition.Position).Length() <= MaxAnimationRange + && (itemPos.Position - TransformSystem.GetMapCoordinates(uid, xform: xform).Position).Length() <= MaxAnimationRange && MetaData(entity).VisibilityMask == MetaData(uid).VisibilityMask) // Don't animate aghost pickups. { var initialPosition = EntityCoordinates.FromMap(coordinateEntity, itemPos, TransformSystem, EntityManager); diff --git a/Content.Shared/Interaction/RotateToFaceSystem.cs b/Content.Shared/Interaction/RotateToFaceSystem.cs index ed950240af6..7f73d3190f9 100644 --- a/Content.Shared/Interaction/RotateToFaceSystem.cs +++ b/Content.Shared/Interaction/RotateToFaceSystem.cs @@ -70,7 +70,7 @@ public bool TryFaceCoordinates(EntityUid user, Vector2 coordinates, TransformCom if (!Resolve(user, ref xform)) return false; - var diff = coordinates - xform.MapPosition.Position; + var diff = coordinates - _transform.GetMapCoordinates(user, xform: xform).Position; if (diff.LengthSquared() <= 0.01f) return true; diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 1f421d0e6f7..8b3431cb024 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -665,14 +665,14 @@ public bool InRangeUnobstructed( else { // We'll still do the raycast from the centres but we'll bump the range as we know they're in range. - originPos = xformA.MapPosition; + originPos = _transform.GetMapCoordinates(origin, xform: xformA); range = (originPos.Position - targetPos.Position).Length(); } } // No fixtures, e.g. wallmounts. else { - originPos = Transform(origin).MapPosition; + originPos = _transform.GetMapCoordinates(origin); var otherParent = Transform(other).ParentUid; targetRot = otherParent.IsValid() ? Transform(otherParent).LocalRotation + otherAngle : otherAngle; } @@ -826,7 +826,7 @@ public bool InRangeUnobstructed( bool popup = false) { Ignored combinedPredicate = e => e == origin || (predicate?.Invoke(e) ?? false); - var originPosition = Transform(origin).MapPosition; + var originPosition = _transform.GetMapCoordinates(origin); var inRange = InRangeUnobstructed(originPosition, other, range, collisionMask, combinedPredicate, ShouldCheckAccess(origin)); if (!inRange && popup && _gameTiming.IsFirstTimePredicted) diff --git a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs index 21861f57dab..03da2d09b0e 100644 --- a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs +++ b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs @@ -77,7 +77,7 @@ public override void Update(float frameTime) // the problem is that stack pickups delete the original entity, which is fine, but due to // game state handling we can't show a lerp animation for it. var nearXform = Transform(near); - var nearMap = nearXform.MapPosition; + var nearMap = _transform.GetMapCoordinates(near, xform: nearXform); var nearCoords = EntityCoordinates.FromMap(moverCoords.EntityId, nearMap, _transform, EntityManager); if (!_storage.Insert(uid, near, out var stacked, storageComp: storage, playSound: !playedSound)) diff --git a/Content.Shared/Tabletop/SharedTabletopSystem.cs b/Content.Shared/Tabletop/SharedTabletopSystem.cs index 7bfd9d34572..afa77a643a0 100644 --- a/Content.Shared/Tabletop/SharedTabletopSystem.cs +++ b/Content.Shared/Tabletop/SharedTabletopSystem.cs @@ -16,7 +16,7 @@ public abstract class SharedTabletopSystem : EntitySystem [Dependency] protected readonly ActionBlockerSystem ActionBlockerSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedTransformSystem _transforms = default!; + [Dependency] protected readonly SharedTransformSystem Transforms = default!; [Dependency] private readonly IMapManager _mapMan = default!; public override void Initialize() @@ -41,8 +41,8 @@ protected virtual void OnTabletopMove(TabletopMoveEvent msg, EntitySessionEventA // Move the entity and dirty it (we use the map ID from the entity so noone can try to be funny and move the item to another map) var transform = EntityManager.GetComponent(moved); - _transforms.SetParent(moved, transform, _mapMan.GetMapEntityId(transform.MapID)); - _transforms.SetLocalPositionNoLerp(transform, msg.Coordinates.Position); + Transforms.SetParent(moved, transform, _mapMan.GetMapEntityId(transform.MapID)); + Transforms.SetLocalPositionNoLerp(transform, msg.Coordinates.Position); } private void OnDraggingPlayerChanged(TabletopDraggingPlayerChangedEvent msg, EntitySessionEventArgs args) diff --git a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs index dd297422c37..ad2249bfddf 100644 --- a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs +++ b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs @@ -220,7 +220,7 @@ protected virtual void StartTether(EntityUid gunUid, BaseForceGunComponent compo _blocker.UpdateCanMove(target); // Invisible tether entity - var tether = Spawn("TetherEntity", Transform(target).MapPosition); + var tether = Spawn("TetherEntity", TransformSystem.GetMapCoordinates(target)); var tetherPhysics = Comp(tether); component.TetherEntity = tether; _physics.WakeBody(tether); diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 11cfc88470a..6242312b070 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -35,7 +35,7 @@ private void OnBallisticUse(EntityUid uid, BallisticAmmoProviderComponent compon if (args.Handled) return; - ManualCycle(uid, component, Transform(uid).MapPosition, args.User); + ManualCycle(uid, component, TransformSystem.GetMapCoordinates(uid), args.User); args.Handled = true; } @@ -161,7 +161,7 @@ private void OnBallisticVerb(EntityUid uid, BallisticAmmoProviderComponent compo { Text = Loc.GetString("gun-ballistic-cycle"), Disabled = GetBallisticShots(component) == 0, - Act = () => ManualCycle(uid, component, Transform(uid).MapPosition, args.User), + Act = () => ManualCycle(uid, component, TransformSystem.GetMapCoordinates(uid), args.User), }); } From 3e277a22d19a0fcf2d45f59f7c885426aa39165e Mon Sep 17 00:00:00 2001 From: no <165581243+pissdemon@users.noreply.github.com> Date: Sun, 12 May 2024 16:35:30 +0200 Subject: [PATCH 131/215] Prevent admin-frozen players from ghosting or suiciding, add "Freeze And Mute" verb (#27813) * prevent admin-frozen players from ghosting or suiciding * Add "Freeze and Mute" admin verb * Allow "Freeze And Mute" admin verb when player is already frozen but not muted * Remove redundant scream handler (scream action just emotes, duh) * AdminFrozenSystem: clean imports * Update Content.Server/Chat/Commands/SuicideCommand.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Ghost.cs * retrigger ci (empty commit) --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../Systems/AdminFrozenSystem.cs | 7 ++ .../Systems/AdminFrozenSystem.cs | 16 +++++ .../Administration/Systems/AdminVerbSystem.cs | 64 ++++++++++++++----- .../Chat/Commands/SuicideCommand.cs | 21 ++++-- Content.Server/Ghost/Ghost.cs | 21 ++++-- .../Administration/AdminFrozenComponent.cs | 9 ++- ...enSystem.cs => SharedAdminFrozenSystem.cs} | 20 +++++- .../en-US/administration/admin-verbs.ftl | 1 + .../en-US/chat/commands/ghost-command.ftl | 5 ++ .../en-US/chat/commands/suicide-command.ftl | 2 + 10 files changed, 138 insertions(+), 28 deletions(-) create mode 100644 Content.Client/Administration/Systems/AdminFrozenSystem.cs create mode 100644 Content.Server/Administration/Systems/AdminFrozenSystem.cs rename Content.Shared/Administration/{AdminFrozenSystem.cs => SharedAdminFrozenSystem.cs} (78%) create mode 100644 Resources/Locale/en-US/chat/commands/ghost-command.ftl diff --git a/Content.Client/Administration/Systems/AdminFrozenSystem.cs b/Content.Client/Administration/Systems/AdminFrozenSystem.cs new file mode 100644 index 00000000000..885585f985c --- /dev/null +++ b/Content.Client/Administration/Systems/AdminFrozenSystem.cs @@ -0,0 +1,7 @@ +using Content.Shared.Administration; + +namespace Content.Client.Administration.Systems; + +public sealed class AdminFrozenSystem : SharedAdminFrozenSystem +{ +} diff --git a/Content.Server/Administration/Systems/AdminFrozenSystem.cs b/Content.Server/Administration/Systems/AdminFrozenSystem.cs new file mode 100644 index 00000000000..baf7b682b85 --- /dev/null +++ b/Content.Server/Administration/Systems/AdminFrozenSystem.cs @@ -0,0 +1,16 @@ +using Content.Shared.Administration; + +namespace Content.Server.Administration.Systems; + +public sealed class AdminFrozenSystem : SharedAdminFrozenSystem +{ + /// + /// Freezes and mutes the given entity. + /// + public void FreezeAndMute(EntityUid uid) + { + var comp = EnsureComp(uid); + comp.Muted = true; + Dirty(uid, comp); + } +} diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.cs b/Content.Server/Administration/Systems/AdminVerbSystem.cs index 5bb75b4c99c..7aa4c8b400a 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.cs @@ -67,6 +67,7 @@ public sealed partial class AdminVerbSystem : EntitySystem [Dependency] private readonly StationSystem _stations = default!; [Dependency] private readonly StationSpawningSystem _spawning = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; + [Dependency] private readonly AdminFrozenSystem _freeze = default!; private readonly Dictionary> _openSolutionUis = new(); @@ -131,24 +132,57 @@ private void AddAdminVerbs(GetVerbsEvent args) args.Verbs.Add(prayerVerb); // Freeze - var frozen = HasComp(args.Target); - args.Verbs.Add(new Verb + var frozen = TryComp(args.Target, out var frozenComp); + var frozenAndMuted = frozenComp?.Muted ?? false; + + if (!frozen) { - Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze. - Text = frozen - ? Loc.GetString("admin-verbs-unfreeze") - : Loc.GetString("admin-verbs-freeze"), - Category = VerbCategory.Admin, - Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")), - Act = () => + args.Verbs.Add(new Verb { - if (frozen) - RemComp(args.Target); - else + Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze. + Text = Loc.GetString("admin-verbs-freeze"), + Category = VerbCategory.Admin, + Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")), + Act = () => + { EnsureComp(args.Target); - }, - Impact = LogImpact.Medium, - }); + }, + Impact = LogImpact.Medium, + }); + } + + if (!frozenAndMuted) + { + // allow you to additionally mute someone when they are already frozen + args.Verbs.Add(new Verb + { + Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze. + Text = Loc.GetString("admin-verbs-freeze-and-mute"), + Category = VerbCategory.Admin, + Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")), + Act = () => + { + _freeze.FreezeAndMute(args.Target); + }, + Impact = LogImpact.Medium, + }); + } + + if (frozen) + { + args.Verbs.Add(new Verb + { + Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze. + Text = Loc.GetString("admin-verbs-unfreeze"), + Category = VerbCategory.Admin, + Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")), + Act = () => + { + RemComp(args.Target); + }, + Impact = LogImpact.Medium, + }); + } // Erase args.Verbs.Add(new Verb diff --git a/Content.Server/Chat/Commands/SuicideCommand.cs b/Content.Server/Chat/Commands/SuicideCommand.cs index c967ba78d7f..0c541b48b7a 100644 --- a/Content.Server/Chat/Commands/SuicideCommand.cs +++ b/Content.Server/Chat/Commands/SuicideCommand.cs @@ -1,4 +1,5 @@ using Content.Server.GameTicking; +using Content.Server.Popups; using Content.Shared.Administration; using Content.Shared.Mind; using Robust.Shared.Console; @@ -26,17 +27,27 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) if (player.Status != SessionStatus.InGame || player.AttachedEntity == null) return; - var minds = IoCManager.Resolve().System(); + var entityManager = IoCManager.Resolve(); + var minds = entityManager.System(); // This check also proves mind not-null for at the end when the mob is ghosted. if (!minds.TryGetMind(player, out var mindId, out var mind) || mind.OwnedEntity is not { Valid: true } victim) { - shell.WriteLine("You don't have a mind!"); + shell.WriteLine(Loc.GetString("suicide-command-no-mind")); return; } - var gameTicker = EntitySystem.Get(); - var suicideSystem = EntitySystem.Get(); + if (entityManager.HasComponent(victim)) + { + var deniedMessage = Loc.GetString("suicide-command-denied"); + shell.WriteLine(deniedMessage); + entityManager.System() + .PopupEntity(deniedMessage, victim, victim); + return; + } + + var gameTicker = entityManager.System(); + var suicideSystem = entityManager.System(); if (suicideSystem.Suicide(victim)) { // Prevent the player from returning to the body. @@ -48,7 +59,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) if (gameTicker.OnGhostAttempt(mindId, true, mind: mind)) return; - shell.WriteLine("You can't ghost right now."); + shell.WriteLine(Loc.GetString("ghost-command-denied")); } } } diff --git a/Content.Server/Ghost/Ghost.cs b/Content.Server/Ghost/Ghost.cs index 1453bf3faa9..69d81d95929 100644 --- a/Content.Server/Ghost/Ghost.cs +++ b/Content.Server/Ghost/Ghost.cs @@ -1,4 +1,5 @@ using Content.Server.GameTicking; +using Content.Server.Popups; using Content.Shared.Administration; using Content.Shared.Mind; using Robust.Shared.Console; @@ -11,15 +12,25 @@ public sealed class Ghost : IConsoleCommand [Dependency] private readonly IEntityManager _entities = default!; public string Command => "ghost"; - public string Description => "Give up on life and become a ghost."; - public string Help => "ghost"; + public string Description => Loc.GetString("ghost-command-description"); + public string Help => Loc.GetString("ghost-command-help-text"); public void Execute(IConsoleShell shell, string argStr, string[] args) { var player = shell.Player; if (player == null) { - shell.WriteLine("You have no session, you can't ghost."); + shell.WriteLine(Loc.GetString("ghost-command-no-session")); + return; + } + + if (player.AttachedEntity is { Valid: true } frozen && + _entities.HasComponent(frozen)) + { + var deniedMessage = Loc.GetString("ghost-command-denied"); + shell.WriteLine(deniedMessage); + _entities.System() + .PopupEntity(deniedMessage, frozen, frozen); return; } @@ -30,9 +41,9 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) mind = _entities.GetComponent(mindId); } - if (!EntitySystem.Get().OnGhostAttempt(mindId, true, true, mind)) + if (!_entities.System().OnGhostAttempt(mindId, true, true, mind)) { - shell.WriteLine("You can't ghost right now."); + shell.WriteLine(Loc.GetString("ghost-command-denied")); } } } diff --git a/Content.Shared/Administration/AdminFrozenComponent.cs b/Content.Shared/Administration/AdminFrozenComponent.cs index 164cf764c86..bfcf1db5261 100644 --- a/Content.Shared/Administration/AdminFrozenComponent.cs +++ b/Content.Shared/Administration/AdminFrozenComponent.cs @@ -2,8 +2,13 @@ namespace Content.Shared.Administration; -[RegisterComponent, Access(typeof(AdminFrozenSystem))] -[NetworkedComponent] +[RegisterComponent, Access(typeof(SharedAdminFrozenSystem))] +[NetworkedComponent, AutoGenerateComponentState] public sealed partial class AdminFrozenComponent : Component { + /// + /// Whether the player is also muted. + /// + [DataField, AutoNetworkedField] + public bool Muted; } diff --git a/Content.Shared/Administration/AdminFrozenSystem.cs b/Content.Shared/Administration/SharedAdminFrozenSystem.cs similarity index 78% rename from Content.Shared/Administration/AdminFrozenSystem.cs rename to Content.Shared/Administration/SharedAdminFrozenSystem.cs index 4ec9600b0bd..2fa22e00052 100644 --- a/Content.Shared/Administration/AdminFrozenSystem.cs +++ b/Content.Shared/Administration/SharedAdminFrozenSystem.cs @@ -1,15 +1,17 @@ using Content.Shared.ActionBlocker; +using Content.Shared.Emoting; using Content.Shared.Interaction.Events; using Content.Shared.Item; using Content.Shared.Movement.Events; using Content.Shared.Movement.Pulling.Components; using Content.Shared.Movement.Pulling.Events; using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Speech; using Content.Shared.Throwing; namespace Content.Shared.Administration; -public sealed class AdminFrozenSystem : EntitySystem +public abstract class SharedAdminFrozenSystem : EntitySystem { [Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly PullingSystem _pulling = default!; @@ -28,6 +30,16 @@ public override void Initialize() SubscribeLocalEvent(OnPullAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnEmoteAttempt); + SubscribeLocalEvent(OnSpeakAttempt); + } + + private void OnSpeakAttempt(EntityUid uid, AdminFrozenComponent component, SpeakAttemptEvent args) + { + if (!component.Muted) + return; + + args.Cancel(); } private void OnAttempt(EntityUid uid, AdminFrozenComponent component, CancellableEntityEventArgs args) @@ -62,4 +74,10 @@ private void UpdateCanMove(EntityUid uid, AdminFrozenComponent component, Entity { _blocker.UpdateCanMove(uid); } + + private void OnEmoteAttempt(EntityUid uid, AdminFrozenComponent component, EmoteAttemptEvent args) + { + if (component.Muted) + args.Cancel(); + } } diff --git a/Resources/Locale/en-US/administration/admin-verbs.ftl b/Resources/Locale/en-US/administration/admin-verbs.ftl index 03f92f7c420..16715087ee4 100644 --- a/Resources/Locale/en-US/administration/admin-verbs.ftl +++ b/Resources/Locale/en-US/administration/admin-verbs.ftl @@ -6,6 +6,7 @@ admin-verbs-admin-logs-entity = Entity Logs admin-verbs-teleport-to = Teleport To admin-verbs-teleport-here = Teleport Here admin-verbs-freeze = Freeze +admin-verbs-freeze-and-mute = Freeze And Mute admin-verbs-unfreeze = Unfreeze admin-verbs-erase = Erase admin-verbs-erase-description = Removes the player from the round and crew manifest and deletes their chat messages. diff --git a/Resources/Locale/en-US/chat/commands/ghost-command.ftl b/Resources/Locale/en-US/chat/commands/ghost-command.ftl new file mode 100644 index 00000000000..08e78d34ce5 --- /dev/null +++ b/Resources/Locale/en-US/chat/commands/ghost-command.ftl @@ -0,0 +1,5 @@ +ghost-command-description = Give up on life and become a ghost. +ghost-command-help-text = The ghost command turns you into a ghost and makes the character you played permanently catatonic. + Please note that you cannot return to your character's body after ghosting. +ghost-command-no-session = You have no session, you can't ghost. +ghost-command-denied = You cannot ghost right now. diff --git a/Resources/Locale/en-US/chat/commands/suicide-command.ftl b/Resources/Locale/en-US/chat/commands/suicide-command.ftl index 6748aa630cd..36e861169b8 100644 --- a/Resources/Locale/en-US/chat/commands/suicide-command.ftl +++ b/Resources/Locale/en-US/chat/commands/suicide-command.ftl @@ -6,3 +6,5 @@ suicide-command-help-text = The suicide command gives you a quick way out of a r suicide-command-default-text-others = {$name} is attempting to bite their own tongue! suicide-command-default-text-self = You attempt to bite your own tongue! suicide-command-already-dead = You can't suicide. You're dead. +suicide-command-no-mind = You have no mind! +suicide-command-denied = You cannot suicide right now. From 3f06b4e5111a14e1ae516b8f586577e1528d6d8c Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 12 May 2024 14:36:37 +0000 Subject: [PATCH 132/215] Automatic changelog update --- Resources/Changelog/Admin.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index c7ef3fd2950..92a38669cfa 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -201,5 +201,17 @@ Entries: id: 26 time: '2024-05-02T06:23:23.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/24945 +- author: pissdemon + changes: + - message: Players frozen by admins can no longer use the "ghost" or "suicide" console + commands. + type: Tweak + - message: Added a "Freeze And Mute" admin verb. In addition to acting like Freeze, + it will mute the targeted player, which prevents them from speaking or emoting + until unfrozen. It can also be used on already frozen but not yet muted players. + type: Add + id: 27 + time: '2024-05-12T14:35:30.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27813 Name: Admin Order: 3 From cd9459883e8fd9aa85c71d4b83c7562a0778e046 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sun, 12 May 2024 07:36:46 -0700 Subject: [PATCH 133/215] Add live templates for networked data field, networked component and auto state component (#27906) * Add live templates for networked data field, networked component and auto state component * Fix field access * Fix readonly --- SpaceStation14.sln.DotSettings | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 0decb9a11b2..800617c0fd7 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -228,6 +228,32 @@ public sealed partial class $CLASS$ : IPrototype { public string ID { get; } = default!; } SS14 + True + True + Networked Auto State Component + True + 0 + True + True + 2.0 + InCSharpFile + nscomp + True + [RegisterComponent, Robust.Shared.GameStates.NetworkedComponent, AutoGenerateComponentState] +[Access(typeof($ACCESS$))] + True + True + Networked Component + True + 0 + True + True + 2.0 + InCSharpFile + ncomp + True + [RegisterComponent, Robust.Shared.GameStates.NetworkedComponent] +[Access(typeof($ACCESS$))] True True IoC [Dependency] @@ -465,6 +491,21 @@ public abstract class $CLASS$ : EntitySystem { } } SS14 + True + True + Automatically Networked Data Field + True + 1 + True + 0 + True + True + 2.0 + InCSharpTypeMember + nfield + True + [DataField, AutoNetworkedField] +public $TYPE$ $NAME$; True True cs From c8ded7032c26d50ac67d17c7344e610bafeced92 Mon Sep 17 00:00:00 2001 From: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Date: Sun, 12 May 2024 15:07:54 +0000 Subject: [PATCH 134/215] Record deletion (#27883) * Allow for Station Records interface for aghosts to delete records * Fix record consoles not working when there are more than 2 crew members. HOW DID NOONE NOTICE THIS SOONER??? * Stop being unconventional --- .../StationRecords/GeneralRecord.xaml | 13 +++++ .../StationRecords/GeneralRecord.xaml.cs | 33 ++++++++++++ ...lStationRecordConsoleBoundUserInterface.cs | 1 + .../GeneralStationRecordConsoleWindow.xaml | 2 +- .../GeneralStationRecordConsoleWindow.xaml.cs | 51 +++---------------- .../GeneralStationRecordConsoleComponent.cs | 6 +++ .../GeneralStationRecordConsoleSystem.cs | 20 ++++++-- .../StationRecords/GeneralRecordsUi.cs | 23 +++++++-- .../general-station-records.ftl | 1 + .../Entities/Mobs/Player/admin_ghost.yml | 1 + 10 files changed, 100 insertions(+), 51 deletions(-) create mode 100644 Content.Client/StationRecords/GeneralRecord.xaml create mode 100644 Content.Client/StationRecords/GeneralRecord.xaml.cs diff --git a/Content.Client/StationRecords/GeneralRecord.xaml b/Content.Client/StationRecords/GeneralRecord.xaml new file mode 100644 index 00000000000..add688c3f38 --- /dev/null +++ b/Content.Client/StationRecords/GeneralRecord.xaml @@ -0,0 +1,13 @@ + + + [DataField] public StationRecordsFilter? Filter; + + /// + /// Whether this Records Console is able to delete entries. + /// + [DataField] + public bool CanDeleteEntries; } diff --git a/Content.Server/StationRecords/Systems/GeneralStationRecordConsoleSystem.cs b/Content.Server/StationRecords/Systems/GeneralStationRecordConsoleSystem.cs index a5202285d99..87246ab6757 100644 --- a/Content.Server/StationRecords/Systems/GeneralStationRecordConsoleSystem.cs +++ b/Content.Server/StationRecords/Systems/GeneralStationRecordConsoleSystem.cs @@ -23,9 +23,22 @@ public override void Initialize() subs.Event(UpdateUserInterface); subs.Event(OnKeySelected); subs.Event(OnFiltersChanged); + subs.Event(OnRecordDelete); }); } + private void OnRecordDelete(Entity ent, ref DeleteStationRecord args) + { + if (!ent.Comp.CanDeleteEntries) + return; + + var owning = _station.GetOwningStation(ent.Owner); + + if (owning != null) + _stationRecords.RemoveRecord(new StationRecordKey(args.Id, owning.Value)); + UpdateUserInterface(ent); // Apparently an event does not get raised for this. + } + private void UpdateUserInterface(Entity ent, ref T args) { UpdateUserInterface(ent); @@ -68,8 +81,9 @@ private void UpdateUserInterface(Entity en case 0: _ui.SetUiState(uid, GeneralStationRecordConsoleKey.Key, new GeneralStationRecordConsoleState()); return; - case 1: - console.ActiveKey = listing.Keys.First(); + default: + if (console.ActiveKey == null) + console.ActiveKey = listing.Keys.First(); break; } @@ -79,7 +93,7 @@ private void UpdateUserInterface(Entity en var key = new StationRecordKey(id, owningStation.Value); _stationRecords.TryGetRecord(key, out var record, stationRecords); - GeneralStationRecordConsoleState newState = new(id, record, listing, console.Filter); + GeneralStationRecordConsoleState newState = new(id, record, listing, console.Filter, ent.Comp.CanDeleteEntries); _ui.SetUiState(uid, GeneralStationRecordConsoleKey.Key, newState); } } diff --git a/Content.Shared/StationRecords/GeneralRecordsUi.cs b/Content.Shared/StationRecords/GeneralRecordsUi.cs index 860454efde5..2105c53df25 100644 --- a/Content.Shared/StationRecords/GeneralRecordsUi.cs +++ b/Content.Shared/StationRecords/GeneralRecordsUi.cs @@ -37,17 +37,22 @@ public sealed class GeneralStationRecordConsoleState : BoundUserInterfaceState public readonly GeneralStationRecord? Record; public readonly Dictionary? RecordListing; public readonly StationRecordsFilter? Filter; + public readonly bool CanDeleteEntries; - public GeneralStationRecordConsoleState(uint? key, GeneralStationRecord? record, - Dictionary? recordListing, StationRecordsFilter? newFilter) + public GeneralStationRecordConsoleState(uint? key, + GeneralStationRecord? record, + Dictionary? recordListing, + StationRecordsFilter? newFilter, + bool canDeleteEntries) { SelectedKey = key; Record = record; RecordListing = recordListing; Filter = newFilter; + CanDeleteEntries = canDeleteEntries; } - public GeneralStationRecordConsoleState() : this(null, null, null, null) + public GeneralStationRecordConsoleState() : this(null, null, null, null, false) { } @@ -69,3 +74,15 @@ public SelectStationRecord(uint? selectedKey) SelectedKey = selectedKey; } } + + +[Serializable, NetSerializable] +public sealed class DeleteStationRecord : BoundUserInterfaceMessage +{ + public DeleteStationRecord(uint id) + { + Id = id; + } + + public readonly uint Id; +} diff --git a/Resources/Locale/en-US/station-records/general-station-records.ftl b/Resources/Locale/en-US/station-records/general-station-records.ftl index e36bfac7cb3..4516a547f4d 100644 --- a/Resources/Locale/en-US/station-records/general-station-records.ftl +++ b/Resources/Locale/en-US/station-records/general-station-records.ftl @@ -16,3 +16,4 @@ general-station-record-prints-filter = Fingerprints general-station-record-dna-filter = DNA general-station-record-console-search-records = Search general-station-record-console-reset-filters = Reset +general-station-record-console-delete = Delete diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index 5cf521264cb..aebaeb2c7f4 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -76,6 +76,7 @@ - type: CargoOrderConsole - type: CrewMonitoringConsole - type: GeneralStationRecordConsole + canDeleteEntries: true - type: DeviceNetwork deviceNetId: Wireless receiveFrequencyId: CrewMonitor From 6c0b4d2f0a619635433b6802b4050d1d5f7c69fa Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 12 May 2024 15:09:00 +0000 Subject: [PATCH 135/215] Automatic changelog update --- Resources/Changelog/Admin.yml | 9 +++++++++ Resources/Changelog/Changelog.yml | 14 +++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 92a38669cfa..c06a6b31254 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -213,5 +213,14 @@ Entries: id: 27 time: '2024-05-12T14:35:30.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27813 +- author: nikthechampiongr + changes: + - message: Admins are now able to remove station records through the records console + interface on their aghost. This will remove the record from the manifest and + criminal records as well. + type: Add + id: 28 + time: '2024-05-12T15:07:54.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27883 Name: Admin Order: 3 diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c5e4d32fa25..ae93e07f82e 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Nairodian - changes: - - message: Welding masks can now be made in the Autolathe. - type: Add - id: 6087 - time: '2024-03-04T22:36:06.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25842 - author: Lawdog changes: - message: Changed the max volume of shot glasses from 7u to 5u to better represent @@ -3870,3 +3863,10 @@ id: 6586 time: '2024-05-11T23:18:21.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27576 +- author: nikthechampiongr + changes: + - message: Fixed records consoles sometimes failing to show crew records. + type: Fix + id: 6587 + time: '2024-05-12T15:07:54.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27883 From 2402c1abf351e386d3593b2e38c71b16448f1ec3 Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Sun, 12 May 2024 15:14:35 -0700 Subject: [PATCH 136/215] Fix borg UI regenerating every tick (#27956) * Fix UI elements being recreated when they didn't need to be * Fix up comparison --- .../Silicons/Borgs/BorgMenu.xaml.cs | 19 ++++++++++++++++- .../Laws/Ui/SiliconLawBoundUserInterface.cs | 21 ++++++++++++++++++- .../Silicons/Laws/SiliconLawPrototype.cs | 7 +++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs index 046d8e299fe..474a83b4530 100644 --- a/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs +++ b/Content.Client/Silicons/Borgs/BorgMenu.xaml.cs @@ -25,6 +25,7 @@ public sealed partial class BorgMenu : FancyWindow public readonly EntityUid Entity; public float AccumulatedTime; private string _lastValidName; + private List _modules = new(); public BorgMenu(EntityUid entity) { @@ -114,7 +115,23 @@ private void UpdateModulePanel() ("actual", _chassis.ModuleCount), ("max", _chassis.MaxModules)); + if (_chassis.ModuleContainer.Count == _modules.Count) + { + var isSame = true; + foreach (var module in _chassis.ModuleContainer.ContainedEntities) + { + if (_modules.Contains(module)) + continue; + isSame = false; + break; + } + + if (isSame) + return; + } + ModuleContainer.Children.Clear(); + _modules.Clear(); foreach (var module in _chassis.ModuleContainer.ContainedEntities) { var control = new BorgModuleControl(module, _entity); @@ -123,6 +140,7 @@ private void UpdateModulePanel() RemoveModuleButtonPressed?.Invoke(module); }; ModuleContainer.AddChild(control); + _modules.Add(module); } } @@ -162,4 +180,3 @@ private void OnNameFocusExit(LineEdit.LineEditEventArgs obj) NameChanged?.Invoke(_lastValidName); } } - diff --git a/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs index 2aee0a38c0f..d150735fa11 100644 --- a/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs +++ b/Content.Client/Silicons/Laws/Ui/SiliconLawBoundUserInterface.cs @@ -1,6 +1,7 @@ +using System.Linq; +using Content.Shared.Silicons.Laws; using Content.Shared.Silicons.Laws.Components; using JetBrains.Annotations; -using Robust.Client.GameObjects; namespace Content.Client.Silicons.Laws.Ui; @@ -10,6 +11,7 @@ public sealed class SiliconLawBoundUserInterface : BoundUserInterface [ViewVariables] private SiliconLawMenu? _menu; private EntityUid _owner; + private List? _laws; public SiliconLawBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { @@ -41,6 +43,23 @@ protected override void UpdateState(BoundUserInterfaceState state) if (state is not SiliconLawBuiState msg) return; + if (_laws != null && _laws.Count == msg.Laws.Count) + { + var isSame = true; + foreach (var law in msg.Laws) + { + if (_laws.Contains(law)) + continue; + isSame = false; + break; + } + + if (isSame) + return; + } + + _laws = msg.Laws.ToList(); + _menu?.Update(_owner, msg); } } diff --git a/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs b/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs index 5e5df448b33..d10b86c2417 100644 --- a/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs +++ b/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs @@ -39,6 +39,13 @@ public int CompareTo(SiliconLaw? other) return Order.CompareTo(other.Order); } + public bool Equals(SiliconLaw other) + { + return LawString == other.LawString + && Order == other.Order + && LawIdentifierOverride == other.LawIdentifierOverride; + } + /// /// Return a shallow clone of this law. /// From 2350de3cbbc2be0057a9a790055933ac9a011246 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 12 May 2024 22:15:41 +0000 Subject: [PATCH 137/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ae93e07f82e..90888f0788a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Lawdog - changes: - - message: Changed the max volume of shot glasses from 7u to 5u to better represent - drinking shots in real life - type: Tweak - id: 6088 - time: '2024-03-04T23:51:12.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25847 - author: deltanedas changes: - message: Rehydrated mobs (carp) now keep their forensics data from their cube/plushie @@ -3870,3 +3862,11 @@ id: 6587 time: '2024-05-12T15:07:54.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27883 +- author: ShadowCommander + changes: + - message: Fixed cyborg laws window and other UIs breaking when the built in light + was on. + type: Fix + id: 6588 + time: '2024-05-12T22:14:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27956 From b6a31003d210674c884dbda5c9f4269c6511ee6d Mon Sep 17 00:00:00 2001 From: Kara Date: Sun, 12 May 2024 17:03:07 -0700 Subject: [PATCH 138/215] Open slot storage keybind can now also close the UI (#27962) --- .../Storage/EntitySystems/SharedStorageSystem.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index e6a1d176976..278758e7fec 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -1437,15 +1437,15 @@ private void OnStackCountChanged(EntityUid uid, MetaDataComponent component, Sta private void HandleOpenBackpack(ICommonSession? session) { - HandleOpenSlotUI(session, "back"); + HandleToggleSlotUI(session, "back"); } private void HandleOpenBelt(ICommonSession? session) { - HandleOpenSlotUI(session, "belt"); + HandleToggleSlotUI(session, "belt"); } - private void HandleOpenSlotUI(ICommonSession? session, string slot) + private void HandleToggleSlotUI(ICommonSession? session, string slot) { if (session is not { } playerSession) return; @@ -1459,7 +1459,14 @@ private void HandleOpenSlotUI(ICommonSession? session, string slot) if (!ActionBlocker.CanInteract(playerEnt, storageEnt)) return; - OpenStorageUI(storageEnt.Value, playerEnt); + if (!_ui.IsUiOpen(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt)) + { + OpenStorageUI(storageEnt.Value, playerEnt); + } + else + { + _ui.CloseUi(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt); + } } protected void ClearCantFillReasons() From 425692e8e372f54831fa40e1096cfa7a255b73dc Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 13 May 2024 00:04:13 +0000 Subject: [PATCH 139/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 90888f0788a..a979fa6f89a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: deltanedas - changes: - - message: Rehydrated mobs (carp) now keep their forensics data from their cube/plushie - form. - type: Tweak - id: 6089 - time: '2024-03-05T03:13:50.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25148 - author: Brandon-Huu changes: - message: Removed the ability for command or any antag-safe role to be initial @@ -3870,3 +3862,11 @@ id: 6588 time: '2024-05-12T22:14:35.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27956 +- author: mirrorcult + changes: + - message: The open slot storage keybinds (V, shift+V) now close the window if it's + already opened + type: Tweak + id: 6589 + time: '2024-05-13T00:03:07.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27962 From b93f95635304d9624ae9ab73d1d5f3149fe2103e Mon Sep 17 00:00:00 2001 From: Kara Date: Sun, 12 May 2024 17:34:52 -0700 Subject: [PATCH 140/215] Resolve `'EntitySystem.Get()' is obsolete` in content (#27936) * PROJECT 0 WARNINGS: Resolve `'EntitySystem.Get()' is obsolete` in content * pass entman * dog ass test * webeditor --- Content.Client/Decals/ToggleDecalCommand.cs | 4 +++- .../Decals/UI/DecalPlacerWindow.xaml.cs | 3 ++- .../Systems/NetworkConfiguratorSystem.cs | 4 +++- Content.Client/NodeContainer/NodeVisCommand.cs | 8 ++++++-- .../Tests/Commands/RestartRoundTest.cs | 4 ++-- Content.Server/Administration/Commands/DSay.cs | 4 +++- .../Administration/Commands/ReadyAll.cs | 4 +++- .../Administration/Commands/ShuttleCommands.cs | 10 +++++++--- .../Commands/ThrowScoreboardCommand.cs | 4 +++- .../Administration/Logs/AdminLogsEui.cs | 3 ++- .../Administration/UI/SpawnExplosionEui.cs | 2 +- Content.Server/Alert/Commands/ClearAlert.cs | 6 ++++-- Content.Server/Alert/Commands/ShowAlert.cs | 6 ++++-- .../Atmos/Commands/ListGasesCommand.cs | 4 +++- .../Atmos/Commands/ShowAtmosCommand.cs | 4 +++- .../Atmos/Components/AtmosPlaqueComponent.cs | 2 +- .../Atmos/Monitor/Systems/AirAlarmModes.cs | 4 ++-- .../Piping/Components/AtmosPipeColorComponent.cs | 2 +- Content.Server/Chat/Commands/LOOCCommand.cs | 4 +++- Content.Server/Chat/Commands/SuicideCommand.cs | 16 ++++++++++------ .../Chemistry/EntitySystems/VaporSystem.cs | 2 +- .../ReactionEffects/ExplosionReactionEffect.cs | 3 ++- .../ReagentEffectConditions/HasTagCondition.cs | 2 +- .../ReagentEffects/ChemCleanBloodstream.cs | 2 +- .../ReagentEffects/ExtinguishReaction.cs | 2 +- .../Chemistry/ReagentEffects/Ignite.cs | 2 +- .../ReagentEffects/ModifyBleedAmount.cs | 2 +- .../Chemistry/ReagentEffects/ModifyBloodLevel.cs | 2 +- .../ReagentEffects/MovespeedModifier.cs | 2 +- .../Chemistry/ReagentEffects/Oxygenate.cs | 2 +- .../Chemistry/ReagentEffects/Paralyze.cs | 2 +- .../Chemistry/ReagentEffects/SatiateThirst.cs | 2 +- .../ReagentEffects/WashCreamPieReaction.cs | 2 +- .../TileReactions/CleanDecalsReaction.cs | 15 ++++++++------- .../Chemistry/TileReactions/CleanTileReaction.cs | 12 +++++++----- .../TileReactions/CreateEntityTileReaction.cs | 12 ++++++------ .../TileReactions/ExtinguishTileReaction.cs | 7 +++++-- .../TileReactions/FlammableTileReaction.cs | 7 +++++-- .../Chemistry/TileReactions/PryTileReaction.cs | 7 +++++-- .../SpillIfPuddlePresentTileReaction.cs | 7 +++++-- .../Chemistry/TileReactions/SpillTileReaction.cs | 7 ++++--- .../Thresholds/Behaviors/EjectVendorItems.cs | 2 +- .../Thresholds/Behaviors/OpenBehavior.cs | 2 +- .../Thresholds/Behaviors/SpillBehavior.cs | 4 ++-- .../Fluids/EntitySystems/PuddleSystem.cs | 2 +- .../Fluids/EntitySystems/SmokeSystem.cs | 2 +- .../GameTicking/Commands/DelayStartCommand.cs | 4 +++- .../GameTicking/Commands/EndRoundCommand.cs | 4 +++- .../GameTicking/Commands/ForcePresetCommand.cs | 4 +++- .../GameTicking/Commands/GoLobbyCommand.cs | 4 +++- .../GameTicking/Commands/ObserveCommand.cs | 4 +++- .../GameTicking/Commands/RestartRoundCommand.cs | 10 +++++++--- .../GameTicking/Commands/StartRoundCommand.cs | 4 +++- .../GameTicking/Commands/ToggleReadyCommand.cs | 4 +++- .../Ghost/Roles/Components/GhostRoleComponent.cs | 6 +++--- Content.Server/Ghost/Roles/GhostRoleSystem.cs | 4 +++- Content.Server/Ghost/Roles/UI/GhostRolesEui.cs | 2 +- .../Maps/Conditions/HolidayMapCondition.cs | 2 +- Content.Server/NodeContainer/Nodes/PipeNode.cs | 6 +++--- .../Power/Commands/PowerStatCommand.cs | 4 +++- Content.Server/Power/PowerWireAction.cs | 2 +- .../Sandbox/Commands/ColorNetworkCommand.cs | 2 +- .../Chemistry/Reaction/ITileReaction.cs | 5 ++++- .../Chemistry/Reagent/ReagentPrototype.cs | 4 ++-- .../Shared/Alert/ServerAlertsComponentTests.cs | 10 +++++----- 65 files changed, 186 insertions(+), 110 deletions(-) diff --git a/Content.Client/Decals/ToggleDecalCommand.cs b/Content.Client/Decals/ToggleDecalCommand.cs index 9f0851f0806..025ed1299d1 100644 --- a/Content.Client/Decals/ToggleDecalCommand.cs +++ b/Content.Client/Decals/ToggleDecalCommand.cs @@ -5,11 +5,13 @@ namespace Content.Client.Decals; public sealed class ToggleDecalCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "toggledecals"; public string Description => "Toggles decaloverlay"; public string Help => $"{Command}"; public void Execute(IConsoleShell shell, string argStr, string[] args) { - EntitySystem.Get().ToggleOverlay(); + _e.System().ToggleOverlay(); } } diff --git a/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs b/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs index 1be17510807..21b816515a4 100644 --- a/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs +++ b/Content.Client/Decals/UI/DecalPlacerWindow.xaml.cs @@ -16,6 +16,7 @@ namespace Content.Client.Decals.UI; public sealed partial class DecalPlacerWindow : DefaultWindow { [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IEntityManager _e = default!; private readonly DecalPlacementSystem _decalPlacementSystem; @@ -39,7 +40,7 @@ public DecalPlacerWindow() RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - _decalPlacementSystem = EntitySystem.Get(); + _decalPlacementSystem = _e.System(); // This needs to be done in C# so we can have custom stuff passed in the constructor // and thus have a proper step size diff --git a/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs b/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs index 2fd4f296556..2d561ba5f21 100644 --- a/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs +++ b/Content.Client/NetworkConfigurator/Systems/NetworkConfiguratorSystem.cs @@ -139,11 +139,13 @@ protected override void FrameUpdate(FrameEventArgs args) public sealed class ClearAllNetworkLinkOverlays : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "clearnetworklinkoverlays"; public string Description => "Clear all network link overlays."; public string Help => Command; public void Execute(IConsoleShell shell, string argStr, string[] args) { - IoCManager.Resolve().System().ClearAllOverlays(); + _e.System().ClearAllOverlays(); } } diff --git a/Content.Client/NodeContainer/NodeVisCommand.cs b/Content.Client/NodeContainer/NodeVisCommand.cs index c6a95fce5b5..249c0d94272 100644 --- a/Content.Client/NodeContainer/NodeVisCommand.cs +++ b/Content.Client/NodeContainer/NodeVisCommand.cs @@ -8,6 +8,8 @@ namespace Content.Client.NodeContainer { public sealed class NodeVisCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "nodevis"; public string Description => "Toggles node group visualization"; public string Help => ""; @@ -21,20 +23,22 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - var sys = EntitySystem.Get(); + var sys = _e.System(); sys.SetVisEnabled(!sys.VisEnabled); } } public sealed class NodeVisFilterCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "nodevisfilter"; public string Description => "Toggles showing a specific group on nodevis"; public string Help => "Usage: nodevis [filter]\nOmit filter to list currently masked-off"; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var sys = EntitySystem.Get(); + var sys = _e.System(); if (args.Length == 0) { diff --git a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs index 74d014b7724..ff24ec09686 100644 --- a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs +++ b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs @@ -3,6 +3,7 @@ using Content.Shared.CCVar; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Timing; namespace Content.IntegrationTests.Tests.Commands @@ -40,8 +41,7 @@ await server.WaitAssertion(() => tickBeforeRestart = entityManager.CurrentTick; - var command = new RestartRoundNowCommand(); - command.Execute(null, string.Empty, Array.Empty()); + gameTicker.RestartRound(); if (lobbyEnabled) { diff --git a/Content.Server/Administration/Commands/DSay.cs b/Content.Server/Administration/Commands/DSay.cs index 61b47d78567..8682614b5fc 100644 --- a/Content.Server/Administration/Commands/DSay.cs +++ b/Content.Server/Administration/Commands/DSay.cs @@ -7,6 +7,8 @@ namespace Content.Server.Administration.Commands [AdminCommand(AdminFlags.Admin)] sealed class DSay : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "dsay"; public string Description => Loc.GetString("dsay-command-description"); @@ -32,7 +34,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) if (string.IsNullOrEmpty(message)) return; - var chat = EntitySystem.Get(); + var chat = _e.System(); chat.TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Dead, false, shell, player); } } diff --git a/Content.Server/Administration/Commands/ReadyAll.cs b/Content.Server/Administration/Commands/ReadyAll.cs index d55ef09660c..530ba0e89c4 100644 --- a/Content.Server/Administration/Commands/ReadyAll.cs +++ b/Content.Server/Administration/Commands/ReadyAll.cs @@ -8,6 +8,8 @@ namespace Content.Server.Administration.Commands [AdminCommand(AdminFlags.Round)] public sealed class ReadyAll : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "readyall"; public string Description => "Readies up all players in the lobby, except for observers."; public string Help => $"{Command} | ̣{Command} "; @@ -20,7 +22,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) ready = bool.Parse(args[0]); } - var gameTicker = EntitySystem.Get(); + var gameTicker = _e.System(); if (gameTicker.RunLevel != GameRunLevel.PreRoundLobby) diff --git a/Content.Server/Administration/Commands/ShuttleCommands.cs b/Content.Server/Administration/Commands/ShuttleCommands.cs index 5b0d39fd154..ea0f891360a 100644 --- a/Content.Server/Administration/Commands/ShuttleCommands.cs +++ b/Content.Server/Administration/Commands/ShuttleCommands.cs @@ -8,6 +8,8 @@ namespace Content.Server.Administration.Commands [AdminCommand(AdminFlags.Round)] public sealed class CallShuttleCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "callshuttle"; public string Description => Loc.GetString("call-shuttle-command-description"); public string Help => Loc.GetString("call-shuttle-command-help-text", ("command",Command)); @@ -19,7 +21,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) // ReSharper disable once ConvertIfStatementToSwitchStatement if (args.Length == 1 && TimeSpan.TryParseExact(args[0], ContentLocalizationManager.TimeSpanMinutesFormats, loc.DefaultCulture, out var timeSpan)) { - EntitySystem.Get().RequestRoundEnd(timeSpan, shell.Player?.AttachedEntity, false); + _e.System().RequestRoundEnd(timeSpan, shell.Player?.AttachedEntity, false); } else if (args.Length == 1) { @@ -27,7 +29,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) } else { - EntitySystem.Get().RequestRoundEnd(shell.Player?.AttachedEntity, false); + _e.System().RequestRoundEnd(shell.Player?.AttachedEntity, false); } } } @@ -35,13 +37,15 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) [AdminCommand(AdminFlags.Round)] public sealed class RecallShuttleCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "recallshuttle"; public string Description => Loc.GetString("recall-shuttle-command-description"); public string Help => Loc.GetString("recall-shuttle-command-help-text", ("command",Command)); public void Execute(IConsoleShell shell, string argStr, string[] args) { - EntitySystem.Get().CancelRoundEndCountdown(shell.Player?.AttachedEntity, false); + _e.System().CancelRoundEndCountdown(shell.Player?.AttachedEntity, false); } } } diff --git a/Content.Server/Administration/Commands/ThrowScoreboardCommand.cs b/Content.Server/Administration/Commands/ThrowScoreboardCommand.cs index ed4ec5a5154..e66efe809aa 100644 --- a/Content.Server/Administration/Commands/ThrowScoreboardCommand.cs +++ b/Content.Server/Administration/Commands/ThrowScoreboardCommand.cs @@ -7,6 +7,8 @@ namespace Content.Server.Administration.Commands; [AdminCommand(AdminFlags.VarEdit)] public sealed class ThrowScoreboardCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "throwscoreboard"; public string Description => Loc.GetString("throw-scoreboard-command-description"); @@ -20,6 +22,6 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) shell.WriteLine(Help); return; } - EntitySystem.Get().ShowRoundEndScoreboard(); + _e.System().ShowRoundEndScoreboard(); } } diff --git a/Content.Server/Administration/Logs/AdminLogsEui.cs b/Content.Server/Administration/Logs/AdminLogsEui.cs index 8bd870b2c77..e98a3b0ff29 100644 --- a/Content.Server/Administration/Logs/AdminLogsEui.cs +++ b/Content.Server/Administration/Logs/AdminLogsEui.cs @@ -23,6 +23,7 @@ public sealed class AdminLogsEui : BaseEui [Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IConfigurationManager _configuration = default!; + [Dependency] private readonly IEntityManager _e = default!; private readonly ISawmill _sawmill; @@ -51,7 +52,7 @@ public AdminLogsEui() }; } - private int CurrentRoundId => EntitySystem.Get().RoundId; + private int CurrentRoundId => _e.System().RoundId; public override async void Opened() { diff --git a/Content.Server/Administration/UI/SpawnExplosionEui.cs b/Content.Server/Administration/UI/SpawnExplosionEui.cs index a3729b14543..001ce1efeec 100644 --- a/Content.Server/Administration/UI/SpawnExplosionEui.cs +++ b/Content.Server/Administration/UI/SpawnExplosionEui.cs @@ -31,7 +31,7 @@ public override void HandleMessage(EuiMessageBase msg) if (request.TotalIntensity <= 0 || request.IntensitySlope <= 0) return; - var explosion = EntitySystem.Get().GenerateExplosionPreview(request); + var explosion = _explosionSystem.GenerateExplosionPreview(request); if (explosion == null) { diff --git a/Content.Server/Alert/Commands/ClearAlert.cs b/Content.Server/Alert/Commands/ClearAlert.cs index 1759612702f..73a6ca52c70 100644 --- a/Content.Server/Alert/Commands/ClearAlert.cs +++ b/Content.Server/Alert/Commands/ClearAlert.cs @@ -9,6 +9,8 @@ namespace Content.Server.Alert.Commands [AdminCommand(AdminFlags.Debug)] public sealed class ClearAlert : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "clearalert"; public string Description => "Clears an alert for a player, defaulting to current player"; public string Help => "clearalert "; @@ -30,14 +32,14 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return; } - if (!IoCManager.Resolve().TryGetComponent(attachedEntity, out AlertsComponent? alertsComponent)) + if (!_e.TryGetComponent(attachedEntity, out AlertsComponent? alertsComponent)) { shell.WriteLine("user has no alerts component"); return; } var alertType = args[0]; - var alertsSystem = EntitySystem.Get(); + var alertsSystem = _e.System(); if (!alertsSystem.TryGet(Enum.Parse(alertType), out var alert)) { shell.WriteLine("unrecognized alertType " + alertType); diff --git a/Content.Server/Alert/Commands/ShowAlert.cs b/Content.Server/Alert/Commands/ShowAlert.cs index 11901e9af00..f37ab23f2fa 100644 --- a/Content.Server/Alert/Commands/ShowAlert.cs +++ b/Content.Server/Alert/Commands/ShowAlert.cs @@ -9,6 +9,8 @@ namespace Content.Server.Alert.Commands [AdminCommand(AdminFlags.Debug)] public sealed class ShowAlert : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "showalert"; public string Description => "Shows an alert for a player, defaulting to current player"; public string Help => "showalert "; @@ -30,7 +32,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return; } - if (!IoCManager.Resolve().TryGetComponent(attachedEntity, out AlertsComponent? alertsComponent)) + if (!_e.TryGetComponent(attachedEntity, out AlertsComponent? alertsComponent)) { shell.WriteLine("user has no alerts component"); return; @@ -38,7 +40,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var alertType = args[0]; var severity = args[1]; - var alertsSystem = EntitySystem.Get(); + var alertsSystem = _e.System(); if (!alertsSystem.TryGet(Enum.Parse(alertType), out var alert)) { shell.WriteLine("unrecognized alertType " + alertType); diff --git a/Content.Server/Atmos/Commands/ListGasesCommand.cs b/Content.Server/Atmos/Commands/ListGasesCommand.cs index ab792fcf81f..81acc2b3b5d 100644 --- a/Content.Server/Atmos/Commands/ListGasesCommand.cs +++ b/Content.Server/Atmos/Commands/ListGasesCommand.cs @@ -8,13 +8,15 @@ namespace Content.Server.Atmos.Commands [AdminCommand(AdminFlags.Debug)] public sealed class ListGasesCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "listgases"; public string Description => "Prints a list of gases and their indices."; public string Help => "listgases"; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var atmosSystem = EntitySystem.Get(); + var atmosSystem = _e.System(); foreach (var gasPrototype in atmosSystem.Gases) { diff --git a/Content.Server/Atmos/Commands/ShowAtmosCommand.cs b/Content.Server/Atmos/Commands/ShowAtmosCommand.cs index 263ef947d0a..89b9c846ece 100644 --- a/Content.Server/Atmos/Commands/ShowAtmosCommand.cs +++ b/Content.Server/Atmos/Commands/ShowAtmosCommand.cs @@ -8,6 +8,8 @@ namespace Content.Server.Atmos.Commands [AdminCommand(AdminFlags.Debug)] public sealed class ShowAtmos : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "showatmos"; public string Description => "Toggles seeing atmos debug overlay."; public string Help => $"Usage: {Command}"; @@ -21,7 +23,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - var atmosDebug = EntitySystem.Get(); + var atmosDebug = _e.System(); var enabled = atmosDebug.ToggleObserver(player); shell.WriteLine(enabled diff --git a/Content.Server/Atmos/Components/AtmosPlaqueComponent.cs b/Content.Server/Atmos/Components/AtmosPlaqueComponent.cs index d86cbe1d2fb..3332d227e45 100644 --- a/Content.Server/Atmos/Components/AtmosPlaqueComponent.cs +++ b/Content.Server/Atmos/Components/AtmosPlaqueComponent.cs @@ -14,7 +14,7 @@ public PlaqueType TypeVV set { Type = value; - EntitySystem.Get().UpdateSign(Owner, this); + IoCManager.Resolve().System().UpdateSign(Owner, this); } } } diff --git a/Content.Server/Atmos/Monitor/Systems/AirAlarmModes.cs b/Content.Server/Atmos/Monitor/Systems/AirAlarmModes.cs index d794be0d560..f979f4b8607 100644 --- a/Content.Server/Atmos/Monitor/Systems/AirAlarmModes.cs +++ b/Content.Server/Atmos/Monitor/Systems/AirAlarmModes.cs @@ -90,8 +90,8 @@ public AirAlarmModeExecutor() { IoCManager.InjectDependencies(this); - DeviceNetworkSystem = EntitySystem.Get(); - AirAlarmSystem = EntitySystem.Get(); + DeviceNetworkSystem = EntityManager.System(); + AirAlarmSystem = EntityManager.System(); } } diff --git a/Content.Server/Atmos/Piping/Components/AtmosPipeColorComponent.cs b/Content.Server/Atmos/Piping/Components/AtmosPipeColorComponent.cs index 455d125e44b..5b05668ad57 100644 --- a/Content.Server/Atmos/Piping/Components/AtmosPipeColorComponent.cs +++ b/Content.Server/Atmos/Piping/Components/AtmosPipeColorComponent.cs @@ -13,7 +13,7 @@ public sealed partial class AtmosPipeColorComponent : Component public Color ColorVV { get => Color; - set => EntitySystem.Get().SetColor(Owner, this, value); + set => IoCManager.Resolve().System().SetColor(Owner, this, value); } } } diff --git a/Content.Server/Chat/Commands/LOOCCommand.cs b/Content.Server/Chat/Commands/LOOCCommand.cs index 9e16193fc38..059d25df27c 100644 --- a/Content.Server/Chat/Commands/LOOCCommand.cs +++ b/Content.Server/Chat/Commands/LOOCCommand.cs @@ -8,6 +8,8 @@ namespace Content.Server.Chat.Commands [AnyCommand] internal sealed class LOOCCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "looc"; public string Description => "Send Local Out Of Character chat messages."; public string Help => "looc "; @@ -33,7 +35,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) if (string.IsNullOrEmpty(message)) return; - EntitySystem.Get().TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Looc, false, shell, player); + _e.System().TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Looc, false, shell, player); } } } diff --git a/Content.Server/Chat/Commands/SuicideCommand.cs b/Content.Server/Chat/Commands/SuicideCommand.cs index 0c541b48b7a..0db03fec79b 100644 --- a/Content.Server/Chat/Commands/SuicideCommand.cs +++ b/Content.Server/Chat/Commands/SuicideCommand.cs @@ -10,6 +10,8 @@ namespace Content.Server.Chat.Commands [AnyCommand] internal sealed class SuicideCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "suicide"; public string Description => Loc.GetString("suicide-command-description"); @@ -27,8 +29,8 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) if (player.Status != SessionStatus.InGame || player.AttachedEntity == null) return; - var entityManager = IoCManager.Resolve(); - var minds = entityManager.System(); + var minds = _e.System(); + // This check also proves mind not-null for at the end when the mob is ghosted. if (!minds.TryGetMind(player, out var mindId, out var mind) || mind.OwnedEntity is not { Valid: true } victim) @@ -37,17 +39,19 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (entityManager.HasComponent(victim)) + + var gameTicker = _e.System(); + var suicideSystem = _e.System(); + + if (_e.HasComponent(victim)) { var deniedMessage = Loc.GetString("suicide-command-denied"); shell.WriteLine(deniedMessage); - entityManager.System() + _e.System() .PopupEntity(deniedMessage, victim, victim); return; } - var gameTicker = entityManager.System(); - var suicideSystem = entityManager.System(); if (suicideSystem.Suicide(victim)) { // Prevent the player from returning to the body. diff --git a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs index 1415b6cad3c..8e12670ca62 100644 --- a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs @@ -122,7 +122,7 @@ private void Update(float frameTime, Entity ent, Entity(reagentQuantity.Reagent.Prototype); var reaction = - reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f); + reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f, EntityManager); if (reaction > reagentQuantity.Quantity) { diff --git a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs index 8a558a3ffdc..2771f79c3a3 100644 --- a/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs +++ b/Content.Server/Chemistry/ReactionEffects/ExplosionReactionEffect.cs @@ -61,7 +61,8 @@ public override void Effect(ReagentEffectArgs args) { var intensity = MathF.Min((float) args.Quantity * IntensityPerUnit, MaxTotalIntensity); - EntitySystem.Get().QueueExplosion( + args.EntityManager.System() + .QueueExplosion( args.SolutionEntity, ExplosionType, intensity, diff --git a/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs b/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs index 52d4d00eb3f..d185c7e0a76 100644 --- a/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs +++ b/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs @@ -18,7 +18,7 @@ public sealed partial class HasTag : ReagentEffectCondition public override bool Condition(ReagentEffectArgs args) { if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var tag)) - return EntitySystem.Get().HasTag(tag, Tag) ^ Invert; + return args.EntityManager.System().HasTag(tag, Tag) ^ Invert; return false; } diff --git a/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs b/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs index 402b30a069b..ace73c84f3a 100644 --- a/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs +++ b/Content.Server/Chemistry/ReagentEffects/ChemCleanBloodstream.cs @@ -26,7 +26,7 @@ public override void Effect(ReagentEffectArgs args) cleanseRate *= args.Scale; - var bloodstreamSys = EntitySystem.Get(); + var bloodstreamSys = args.EntityManager.System(); bloodstreamSys.FlushChemicals(args.SolutionEntity, args.Reagent.ID, cleanseRate); } } diff --git a/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs b/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs index e9029089988..69aaf5cdcd7 100644 --- a/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs +++ b/Content.Server/Chemistry/ReagentEffects/ExtinguishReaction.cs @@ -16,7 +16,7 @@ public override void Effect(ReagentEffectArgs args) { if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out FlammableComponent? flammable)) return; - var flammableSystem = EntitySystem.Get(); + var flammableSystem = args.EntityManager.System(); flammableSystem.Extinguish(args.SolutionEntity, flammable); flammableSystem.AdjustFireStacks(args.SolutionEntity, -1.5f * (float) args.Quantity, flammable); } diff --git a/Content.Server/Chemistry/ReagentEffects/Ignite.cs b/Content.Server/Chemistry/ReagentEffects/Ignite.cs index 1fc90f63e31..6c5d47711f3 100644 --- a/Content.Server/Chemistry/ReagentEffects/Ignite.cs +++ b/Content.Server/Chemistry/ReagentEffects/Ignite.cs @@ -19,7 +19,7 @@ public sealed partial class Ignite : ReagentEffect public override void Effect(ReagentEffectArgs args) { - var flamSys = EntitySystem.Get(); + var flamSys = args.EntityManager.System(); flamSys.Ignite(args.SolutionEntity, args.OrganEntity ?? args.SolutionEntity); } } diff --git a/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs b/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs index 7b966ea4788..ecd9c86255f 100644 --- a/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs +++ b/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs @@ -21,7 +21,7 @@ public override void Effect(ReagentEffectArgs args) { if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var blood)) { - var sys = EntitySystem.Get(); + var sys = args.EntityManager.System(); var amt = Scaled ? Amount * args.Quantity.Float() : Amount; amt *= args.Scale; diff --git a/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs b/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs index 748aa71083e..b136ff9bb8c 100644 --- a/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs +++ b/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs @@ -22,7 +22,7 @@ public override void Effect(ReagentEffectArgs args) { if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var blood)) { - var sys = EntitySystem.Get(); + var sys = args.EntityManager.System(); var amt = Scaled ? Amount * args.Quantity : Amount; amt *= args.Scale; diff --git a/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs index 0301742c5af..e538a011f60 100644 --- a/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs +++ b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs @@ -59,7 +59,7 @@ public override void Effect(ReagentEffectArgs args) IncreaseTimer(status, statusLifetime); if (modified) - EntitySystem.Get().RefreshMovementSpeedModifiers(args.SolutionEntity); + args.EntityManager.System().RefreshMovementSpeedModifiers(args.SolutionEntity); } public void IncreaseTimer(MovespeedModifierMetabolismComponent status, float time) diff --git a/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs b/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs index 34a2454139e..6c5ab155e44 100644 --- a/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs +++ b/Content.Server/Chemistry/ReagentEffects/Oxygenate.cs @@ -18,7 +18,7 @@ public override void Effect(ReagentEffectArgs args) { if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var resp)) { - var respSys = EntitySystem.Get(); + var respSys = args.EntityManager.System(); respSys.UpdateSaturation(args.SolutionEntity, args.Quantity.Float() * Factor, resp); } } diff --git a/Content.Server/Chemistry/ReagentEffects/Paralyze.cs b/Content.Server/Chemistry/ReagentEffects/Paralyze.cs index 077d1abf2c2..d5d913f37d2 100644 --- a/Content.Server/Chemistry/ReagentEffects/Paralyze.cs +++ b/Content.Server/Chemistry/ReagentEffects/Paralyze.cs @@ -23,7 +23,7 @@ public override void Effect(ReagentEffectArgs args) var paralyzeTime = ParalyzeTime; paralyzeTime *= args.Scale; - EntitySystem.Get().TryParalyze(args.SolutionEntity, TimeSpan.FromSeconds(paralyzeTime), Refresh); + args.EntityManager.System().TryParalyze(args.SolutionEntity, TimeSpan.FromSeconds(paralyzeTime), Refresh); } } diff --git a/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs b/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs index 1208e74367b..8813813c240 100644 --- a/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs +++ b/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs @@ -23,7 +23,7 @@ public override void Effect(ReagentEffectArgs args) { var uid = args.SolutionEntity; if (args.EntityManager.TryGetComponent(uid, out ThirstComponent? thirst)) - EntitySystem.Get().ModifyThirst(uid, thirst, HydrationFactor); + args.EntityManager.System().ModifyThirst(uid, thirst, HydrationFactor); } protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) diff --git a/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs b/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs index 9bf6792eca2..a71a42c25ff 100644 --- a/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs +++ b/Content.Server/Chemistry/ReagentEffects/WashCreamPieReaction.cs @@ -16,7 +16,7 @@ public override void Effect(ReagentEffectArgs args) { if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out CreamPiedComponent? creamPied)) return; - EntitySystem.Get().SetCreamPied(args.SolutionEntity, creamPied, false); + args.EntityManager.System().SetCreamPied(args.SolutionEntity, creamPied, false); } } } diff --git a/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs b/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs index a20f54728ee..6958dabb81b 100644 --- a/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CleanDecalsReaction.cs @@ -21,19 +21,20 @@ public sealed partial class CleanDecalsReaction : ITileReaction [DataField] public FixedPoint2 CleanCost { get; private set; } = FixedPoint2.New(0.25f); - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { - var entMan = IoCManager.Resolve(); - if (reactVolume <= CleanCost || - !entMan.TryGetComponent(tile.GridUid, out var grid) || - !entMan.TryGetComponent(tile.GridUid, out var decalGrid)) + !entityManager.TryGetComponent(tile.GridUid, out var grid) || + !entityManager.TryGetComponent(tile.GridUid, out var decalGrid)) { return FixedPoint2.Zero; } - var lookupSystem = entMan.System(); - var decalSystem = entMan.System(); + var lookupSystem = entityManager.System(); + var decalSystem = entityManager.System(); // Very generous hitbox. var decals = decalSystem .GetDecalsIntersecting(tile.GridUid, lookupSystem.GetLocalBounds(tile, grid.TileSize).Enlarged(0.5f).Translated(new Vector2(-0.5f, -0.5f))); diff --git a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs index 3f5ae63c365..08f77de72dd 100644 --- a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs @@ -31,12 +31,14 @@ public sealed partial class CleanTileReaction : ITileReaction [DataField("reagent", customTypeSerializer: typeof(PrototypeIdSerializer))] public string ReplacementReagent = "Water"; - FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + FixedPoint2 ITileReaction.TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { - var entMan = IoCManager.Resolve(); - var entities = entMan.System().GetLocalEntitiesIntersecting(tile, 0f).ToArray(); - var puddleQuery = entMan.GetEntityQuery(); - var solutionContainerSystem = entMan.System(); + var entities = entityManager.System().GetLocalEntitiesIntersecting(tile, 0f).ToArray(); + var puddleQuery = entityManager.GetEntityQuery(); + var solutionContainerSystem = entityManager.System(); // Multiply as the amount we can actually purge is higher than the react amount. var purgeAmount = reactVolume / CleanAmountMultiplier; diff --git a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs index 9cb1ba201d5..29f9275bdf4 100644 --- a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs @@ -35,13 +35,13 @@ public sealed partial class CreateEntityTileReaction : ITileReaction [DataField] public float RandomOffsetMax = 0.0f; - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { if (reactVolume >= Usage) { - // TODO probably pass this in args like reagenteffects do. - var entMan = IoCManager.Resolve(); - if (Whitelist != null) { int acc = 0; @@ -59,9 +59,9 @@ public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 var xoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); var yoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); - var center = entMan.System().GetTileCenter(tile); + var center = entityManager.System().GetTileCenter(tile); var pos = center.Offset(new Vector2(xoffs, yoffs)); - entMan.SpawnEntity(Entity, pos); + entityManager.SpawnEntity(Entity, pos); return Usage; } diff --git a/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs b/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs index 312b88bd341..198f650ac1a 100644 --- a/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs @@ -14,12 +14,15 @@ public sealed partial class ExtinguishTileReaction : ITileReaction { [DataField("coolingTemperature")] private float _coolingTemperature = 2f; - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty) return FixedPoint2.Zero; - var atmosphereSystem = EntitySystem.Get(); + var atmosphereSystem = entityManager.System(); var environment = atmosphereSystem.GetTileMixture(tile.GridUid, null, tile.GridIndices, true); diff --git a/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs b/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs index a694bf60190..b13b70d3d5c 100644 --- a/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs @@ -13,12 +13,15 @@ public sealed partial class FlammableTileReaction : ITileReaction { [DataField("temperatureMultiplier")] private float _temperatureMultiplier = 1.15f; - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { if (reactVolume <= FixedPoint2.Zero || tile.Tile.IsEmpty) return FixedPoint2.Zero; - var atmosphereSystem = EntitySystem.Get(); + var atmosphereSystem = entityManager.System(); var environment = atmosphereSystem.GetTileMixture(tile.GridUid, null, tile.GridIndices, true); if (environment == null || !atmosphereSystem.IsHotspotActive(tile.GridUid, tile.GridIndices)) diff --git a/Content.Server/Chemistry/TileReactions/PryTileReaction.cs b/Content.Server/Chemistry/TileReactions/PryTileReaction.cs index 39e95df5f7c..c10b031720f 100644 --- a/Content.Server/Chemistry/TileReactions/PryTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/PryTileReaction.cs @@ -12,9 +12,12 @@ namespace Content.Server.Chemistry.TileReactions; [DataDefinition] public sealed partial class PryTileReaction : ITileReaction { - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { - var sys = IoCManager.Resolve().System(); + var sys = entityManager.System(); sys.PryTile(tile); return reactVolume; } diff --git a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs index 82ede720cd0..6b46b894959 100644 --- a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs @@ -12,9 +12,12 @@ namespace Content.Server.Chemistry.TileReactions [DataDefinition] public sealed partial class SpillIfPuddlePresentTileReaction : ITileReaction { - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { - var spillSystem = EntitySystem.Get(); + var spillSystem = entityManager.System(); if (reactVolume < 5 || !spillSystem.TryGetPuddle(tile, out _)) return FixedPoint2.Zero; diff --git a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs index 8f8b0626a28..fadc7147c9c 100644 --- a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs @@ -26,13 +26,14 @@ public sealed partial class SpillTileReaction : ITileReaction /// [DataField("superSlippery")] private bool _superSlippery; - public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) + public FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager) { if (reactVolume < 5) return FixedPoint2.Zero; - var entityManager = IoCManager.Resolve(); - if (entityManager.EntitySysManager.GetEntitySystem() .TrySpillAt(tile, new Solution(reagent.ID, reactVolume), out var puddleUid, false, false)) { diff --git a/Content.Server/Destructible/Thresholds/Behaviors/EjectVendorItems.cs b/Content.Server/Destructible/Thresholds/Behaviors/EjectVendorItems.cs index 2ffd9a8f9f5..a5212dfe85c 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/EjectVendorItems.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/EjectVendorItems.cs @@ -29,7 +29,7 @@ public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause !system.EntityManager.TryGetComponent(owner, out var xform)) return; - var vendingMachineSystem = EntitySystem.Get(); + var vendingMachineSystem = system.EntityManager.System(); var inventory = vendingMachineSystem.GetAvailableInventory(owner, vendingcomp); if (inventory.Count <= 0) return; diff --git a/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs index 7ab1fe11b01..dffcac6d96e 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/OpenBehavior.cs @@ -11,7 +11,7 @@ public sealed partial class OpenBehavior : IThresholdBehavior { public void Execute(EntityUid uid, DestructibleSystem system, EntityUid? cause = null) { - var openable = EntitySystem.Get(); + var openable = system.EntityManager.System(); openable.TryOpen(uid); } } diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs index 38564380c04..5826a3871db 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs @@ -22,8 +22,8 @@ public sealed partial class SpillBehavior : IThresholdBehavior /// public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) { - var solutionContainerSystem = EntitySystem.Get(); - var spillableSystem = EntitySystem.Get(); + var solutionContainerSystem = system.EntityManager.System(); + var spillableSystem = system.EntityManager.System(); var coordinates = system.EntityManager.GetComponent(owner).Coordinates; diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index 923210cc73c..a232ed8c0eb 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -712,7 +712,7 @@ public void DoTileReactions(TileRef tileRef, Solution solution) { var (reagent, quantity) = solution.Contents[i]; var proto = _prototypeManager.Index(reagent.Prototype); - var removed = proto.ReactionTile(tileRef, quantity); + var removed = proto.ReactionTile(tileRef, quantity, EntityManager); if (removed <= FixedPoint2.Zero) continue; diff --git a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs index ae170842a0c..9638dabf28e 100644 --- a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs +++ b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs @@ -315,7 +315,7 @@ private void ReactOnTile(EntityUid uid, SmokeComponent? component = null, Transf continue; var reagent = _prototype.Index(reagentQuantity.Reagent.Prototype); - reagent.ReactionTile(tile, reagentQuantity.Quantity); + reagent.ReactionTile(tile, reagentQuantity.Quantity, EntityManager); } } diff --git a/Content.Server/GameTicking/Commands/DelayStartCommand.cs b/Content.Server/GameTicking/Commands/DelayStartCommand.cs index 6f57f561d85..6e101d93a32 100644 --- a/Content.Server/GameTicking/Commands/DelayStartCommand.cs +++ b/Content.Server/GameTicking/Commands/DelayStartCommand.cs @@ -7,13 +7,15 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] sealed class DelayStartCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "delaystart"; public string Description => "Delays the round start."; public string Help => $"Usage: {Command} \nPauses/Resumes the countdown if no argument is provided."; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel != GameRunLevel.PreRoundLobby) { shell.WriteLine("This can only be executed while the game is in the pre-round lobby."); diff --git a/Content.Server/GameTicking/Commands/EndRoundCommand.cs b/Content.Server/GameTicking/Commands/EndRoundCommand.cs index 972fa9561a3..c6a8ddbf537 100644 --- a/Content.Server/GameTicking/Commands/EndRoundCommand.cs +++ b/Content.Server/GameTicking/Commands/EndRoundCommand.cs @@ -7,13 +7,15 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] sealed class EndRoundCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "endround"; public string Description => "Ends the round and moves the server to PostRound."; public string Help => String.Empty; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel != GameRunLevel.InRound) { diff --git a/Content.Server/GameTicking/Commands/ForcePresetCommand.cs b/Content.Server/GameTicking/Commands/ForcePresetCommand.cs index 1041fed6ec1..5ef72f59b37 100644 --- a/Content.Server/GameTicking/Commands/ForcePresetCommand.cs +++ b/Content.Server/GameTicking/Commands/ForcePresetCommand.cs @@ -10,13 +10,15 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] sealed class ForcePresetCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "forcepreset"; public string Description => "Forces a specific game preset to start for the current lobby."; public string Help => $"Usage: {Command} "; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel != GameRunLevel.PreRoundLobby) { shell.WriteLine("This can only be executed while the game is in the pre-round lobby."); diff --git a/Content.Server/GameTicking/Commands/GoLobbyCommand.cs b/Content.Server/GameTicking/Commands/GoLobbyCommand.cs index 4932dbe0bff..b5984d260d3 100644 --- a/Content.Server/GameTicking/Commands/GoLobbyCommand.cs +++ b/Content.Server/GameTicking/Commands/GoLobbyCommand.cs @@ -10,6 +10,8 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] public sealed class GoLobbyCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "golobby"; public string Description => "Enables the lobby and restarts the round."; public string Help => $"Usage: {Command} / {Command} "; @@ -18,7 +20,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) GamePresetPrototype? preset = null; var presetName = string.Join(" ", args); - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (args.Length > 0) { diff --git a/Content.Server/GameTicking/Commands/ObserveCommand.cs b/Content.Server/GameTicking/Commands/ObserveCommand.cs index 747e54e28be..16c2c3261de 100644 --- a/Content.Server/GameTicking/Commands/ObserveCommand.cs +++ b/Content.Server/GameTicking/Commands/ObserveCommand.cs @@ -7,6 +7,8 @@ namespace Content.Server.GameTicking.Commands [AnyCommand] sealed class ObserveCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "observe"; public string Description => ""; public string Help => ""; @@ -18,7 +20,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel == GameRunLevel.PreRoundLobby) { diff --git a/Content.Server/GameTicking/Commands/RestartRoundCommand.cs b/Content.Server/GameTicking/Commands/RestartRoundCommand.cs index 36c0ba981c7..e4ea3fa53c3 100644 --- a/Content.Server/GameTicking/Commands/RestartRoundCommand.cs +++ b/Content.Server/GameTicking/Commands/RestartRoundCommand.cs @@ -8,13 +8,15 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] public sealed class RestartRoundCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "restartround"; public string Description => "Ends the current round and starts the countdown for the next lobby."; public string Help => string.Empty; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel != GameRunLevel.InRound) { @@ -22,20 +24,22 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - EntitySystem.Get().EndRound(); + _e.System().EndRound(); } } [AdminCommand(AdminFlags.Round)] public sealed class RestartRoundNowCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "restartroundnow"; public string Description => "Moves the server from PostRound to a new PreRoundLobby."; public string Help => String.Empty; public void Execute(IConsoleShell shell, string argStr, string[] args) { - EntitySystem.Get().RestartRound(); + _e.System().RestartRound(); } } } diff --git a/Content.Server/GameTicking/Commands/StartRoundCommand.cs b/Content.Server/GameTicking/Commands/StartRoundCommand.cs index 1652a141203..432cdd23e35 100644 --- a/Content.Server/GameTicking/Commands/StartRoundCommand.cs +++ b/Content.Server/GameTicking/Commands/StartRoundCommand.cs @@ -7,13 +7,15 @@ namespace Content.Server.GameTicking.Commands [AdminCommand(AdminFlags.Round)] sealed class StartRoundCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "startround"; public string Description => "Ends PreRoundLobby state and starts the round."; public string Help => String.Empty; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var ticker = EntitySystem.Get(); + var ticker = _e.System(); if (ticker.RunLevel != GameRunLevel.PreRoundLobby) { diff --git a/Content.Server/GameTicking/Commands/ToggleReadyCommand.cs b/Content.Server/GameTicking/Commands/ToggleReadyCommand.cs index df418c27ee1..34b504acbc9 100644 --- a/Content.Server/GameTicking/Commands/ToggleReadyCommand.cs +++ b/Content.Server/GameTicking/Commands/ToggleReadyCommand.cs @@ -6,6 +6,8 @@ namespace Content.Server.GameTicking.Commands [AnyCommand] sealed class ToggleReadyCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "toggleready"; public string Description => ""; public string Help => ""; @@ -23,7 +25,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - var ticker = EntitySystem.Get(); + var ticker = _e.System(); ticker.ToggleReady(player, bool.Parse(args[0])); } } diff --git a/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs b/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs index ccd460a9cfc..86026b230ba 100644 --- a/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs +++ b/Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs @@ -40,7 +40,7 @@ public string RoleName set { _roleName = value; - EntitySystem.Get().UpdateAllEui(); + IoCManager.Resolve().System().UpdateAllEui(); } } @@ -52,7 +52,7 @@ public string RoleDescription set { _roleDescription = value; - EntitySystem.Get().UpdateAllEui(); + IoCManager.Resolve().System().UpdateAllEui(); } } @@ -64,7 +64,7 @@ public string RoleRules set { _roleRules = value; - EntitySystem.Get().UpdateAllEui(); + IoCManager.Resolve().System().UpdateAllEui(); } } diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index f8b41fd9ba4..40e5af78596 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -792,13 +792,15 @@ public void SetMode(EntityUid uid, GhostRolePrototype prototype, string verbText [AnyCommand] public sealed class GhostRoles : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "ghostroles"; public string Description => "Opens the ghost role request window."; public string Help => $"{Command}"; public void Execute(IConsoleShell shell, string argStr, string[] args) { if (shell.Player != null) - EntitySystem.Get().OpenEui(shell.Player); + _e.System().OpenEui(shell.Player); else shell.WriteLine("You can only open the ghost roles UI on a client."); } diff --git a/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs b/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs index c1e39919a2f..825691fe950 100644 --- a/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs +++ b/Content.Server/Ghost/Roles/UI/GhostRolesEui.cs @@ -40,7 +40,7 @@ public override void Closed() { base.Closed(); - EntitySystem.Get().CloseEui(Player); + _ghostRoleSystem.CloseEui(Player); } } } diff --git a/Content.Server/Maps/Conditions/HolidayMapCondition.cs b/Content.Server/Maps/Conditions/HolidayMapCondition.cs index 81ecfb356e8..da971c78f32 100644 --- a/Content.Server/Maps/Conditions/HolidayMapCondition.cs +++ b/Content.Server/Maps/Conditions/HolidayMapCondition.cs @@ -10,7 +10,7 @@ public sealed partial class HolidayMapCondition : GameMapCondition public override bool Check(GameMapPrototype map) { - var holidaySystem = EntitySystem.Get(); + var holidaySystem = IoCManager.Resolve().System(); return Holidays.Any(holiday => holidaySystem.IsCurrentlyHoliday(holiday)) ^ Inverted; } diff --git a/Content.Server/NodeContainer/Nodes/PipeNode.cs b/Content.Server/NodeContainer/Nodes/PipeNode.cs index 14afa2f1083..861f3eea98d 100644 --- a/Content.Server/NodeContainer/Nodes/PipeNode.cs +++ b/Content.Server/NodeContainer/Nodes/PipeNode.cs @@ -37,7 +37,7 @@ public void AddAlwaysReachable(PipeNode pipeNode) _alwaysReachable.Add(pipeNode); if (NodeGroup != null) - EntitySystem.Get().QueueRemakeGroup((BaseNodeGroup) NodeGroup); + IoCManager.Resolve().System().QueueRemakeGroup((BaseNodeGroup) NodeGroup); } public void RemoveAlwaysReachable(PipeNode pipeNode) @@ -47,7 +47,7 @@ public void RemoveAlwaysReachable(PipeNode pipeNode) _alwaysReachable.Remove(pipeNode); if (NodeGroup != null) - EntitySystem.Get().QueueRemakeGroup((BaseNodeGroup) NodeGroup); + IoCManager.Resolve().System().QueueRemakeGroup((BaseNodeGroup) NodeGroup); } /// @@ -62,7 +62,7 @@ public bool ConnectionsEnabled _connectionsEnabled = value; if (NodeGroup != null) - EntitySystem.Get().QueueRemakeGroup((BaseNodeGroup) NodeGroup); + IoCManager.Resolve().System().QueueRemakeGroup((BaseNodeGroup) NodeGroup); } } diff --git a/Content.Server/Power/Commands/PowerStatCommand.cs b/Content.Server/Power/Commands/PowerStatCommand.cs index 440e40437b2..127050393aa 100644 --- a/Content.Server/Power/Commands/PowerStatCommand.cs +++ b/Content.Server/Power/Commands/PowerStatCommand.cs @@ -8,13 +8,15 @@ namespace Content.Server.Power.Commands [AdminCommand(AdminFlags.Debug)] public sealed class PowerStatCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; + public string Command => "powerstat"; public string Description => "Shows statistics for pow3r"; public string Help => "Usage: powerstat"; public void Execute(IConsoleShell shell, string argStr, string[] args) { - var stats = EntitySystem.Get().GetStatistics(); + var stats = _e.System().GetStatistics(); shell.WriteLine($"networks: {stats.CountNetworks}"); shell.WriteLine($"loads: {stats.CountLoads}"); diff --git a/Content.Server/Power/PowerWireAction.cs b/Content.Server/Power/PowerWireAction.cs index 785eac91dba..ac34966036c 100644 --- a/Content.Server/Power/PowerWireAction.cs +++ b/Content.Server/Power/PowerWireAction.cs @@ -159,7 +159,7 @@ public override void Initialize() { base.Initialize(); - _electrocutionSystem = EntitySystem.Get(); + _electrocutionSystem = EntityManager.System(); } // This should add a wire into the entity's state, whether it be diff --git a/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs b/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs index 6ce8edd1d84..d5dca64eaac 100644 --- a/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs +++ b/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs @@ -19,7 +19,7 @@ public sealed class ColorNetworkCommand : IConsoleCommand public void Execute(IConsoleShell shell, string argStr, string[] args) { - var sandboxManager = EntitySystem.Get(); + var sandboxManager = _entManager.System(); var adminManager = IoCManager.Resolve(); if (shell.IsClient && (!sandboxManager.IsSandboxEnabled && !adminManager.HasAdminFlag(shell.Player!, AdminFlags.Mapping))) { diff --git a/Content.Shared/Chemistry/Reaction/ITileReaction.cs b/Content.Shared/Chemistry/Reaction/ITileReaction.cs index 1f6a6bf3073..af6cc34f48a 100644 --- a/Content.Shared/Chemistry/Reaction/ITileReaction.cs +++ b/Content.Shared/Chemistry/Reaction/ITileReaction.cs @@ -6,6 +6,9 @@ namespace Content.Shared.Chemistry.Reaction { public interface ITileReaction { - FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume); + FixedPoint2 TileReact(TileRef tile, + ReagentPrototype reagent, + FixedPoint2 reactVolume, + IEntityManager entityManager); } } diff --git a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs index df1b1aa20b4..6095676b9e0 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs @@ -142,7 +142,7 @@ public sealed partial class ReagentPrototype : IPrototype, IInheritingPrototype [DataField] public SoundSpecifier FootstepSound = new SoundCollectionSpecifier("FootstepWater", AudioParams.Default.WithVolume(6)); - public FixedPoint2 ReactionTile(TileRef tile, FixedPoint2 reactVolume) + public FixedPoint2 ReactionTile(TileRef tile, FixedPoint2 reactVolume, IEntityManager entityManager) { var removed = FixedPoint2.Zero; @@ -151,7 +151,7 @@ public FixedPoint2 ReactionTile(TileRef tile, FixedPoint2 reactVolume) foreach (var reaction in TileReactions) { - removed += reaction.TileReact(tile, this, reactVolume - removed); + removed += reaction.TileReact(tile, this, reactVolume - removed, entityManager); if (removed > reactVolume) throw new Exception("Removed more than we have!"); diff --git a/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs b/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs index 405c0ec89ed..47ae3ef74a6 100644 --- a/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs +++ b/Content.Tests/Shared/Alert/ServerAlertsComponentTests.cs @@ -49,10 +49,10 @@ public void ShowAlerts() var alertsComponent = new AlertsComponent(); alertsComponent = IoCManager.InjectDependencies(alertsComponent); - Assert.That(EntitySystem.Get().TryGet(AlertType.LowPressure, out var lowpressure)); - Assert.That(EntitySystem.Get().TryGet(AlertType.HighPressure, out var highpressure)); + Assert.That(entManager.System().TryGet(AlertType.LowPressure, out var lowpressure)); + Assert.That(entManager.System().TryGet(AlertType.HighPressure, out var highpressure)); - EntitySystem.Get().ShowAlert(alertsComponent.Owner, AlertType.LowPressure, null, null); + entManager.System().ShowAlert(alertsComponent.Owner, AlertType.LowPressure, null, null); var getty = new ComponentGetState(); entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); @@ -62,7 +62,7 @@ public void ShowAlerts() Assert.That(alertState.Alerts.Count, Is.EqualTo(1)); Assert.That(alertState.Alerts.ContainsKey(lowpressure.AlertKey)); - EntitySystem.Get().ShowAlert(alertsComponent.Owner, AlertType.HighPressure, null, null); + entManager.System().ShowAlert(alertsComponent.Owner, AlertType.HighPressure, null, null); // Lazy entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); @@ -70,7 +70,7 @@ public void ShowAlerts() Assert.That(alertState.Alerts.Count, Is.EqualTo(1)); Assert.That(alertState.Alerts.ContainsKey(highpressure.AlertKey)); - EntitySystem.Get().ClearAlertCategory(alertsComponent.Owner, AlertCategory.Pressure); + entManager.System().ClearAlertCategory(alertsComponent.Owner, AlertCategory.Pressure); entManager.EventBus.RaiseComponentEvent(alertsComponent, getty); alertState = (AlertsComponent.AlertsComponent_AutoState) getty.State!; From 6e46148d00516102765f84425869c20158f19069 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 12 May 2024 21:07:52 -0400 Subject: [PATCH 141/215] fix weird behavior with storage HUD buttons (#27961) --- Content.Shared/Inventory/InventorySystem.Slots.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Inventory/InventorySystem.Slots.cs b/Content.Shared/Inventory/InventorySystem.Slots.cs index e0f2a695576..19831278b0a 100644 --- a/Content.Shared/Inventory/InventorySystem.Slots.cs +++ b/Content.Shared/Inventory/InventorySystem.Slots.cs @@ -15,7 +15,7 @@ public partial class InventorySystem : EntitySystem private void InitializeSlots() { SubscribeLocalEvent(OnInit); - SubscribeNetworkEvent(OnOpenSlotStorage); + SubscribeAllEvent(OnOpenSlotStorage); _vvm.GetTypeHandler() .AddHandler(HandleViewVariablesSlots, ListViewVariablesSlots); From 93a6823bbbbc0a5ac7f6c5880278998518de8f08 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Mon, 13 May 2024 14:05:37 +1000 Subject: [PATCH 142/215] Implement Equals for ApcBoundInterfaceState (#27965) * Implement Equals for ApcBoundInterfaceState Saves a lot on bandwidth. Also made it round to the nearest 5. * Also this --- .../Power/EntitySystems/ApcSystem.cs | 7 ++++- Content.Shared/APC/SharedApc.cs | 29 ++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Content.Server/Power/EntitySystems/ApcSystem.cs b/Content.Server/Power/EntitySystems/ApcSystem.cs index 388f65ad2e8..529d4d81d7c 100644 --- a/Content.Server/Power/EntitySystems/ApcSystem.cs +++ b/Content.Server/Power/EntitySystems/ApcSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; using Content.Shared.Popups; +using Content.Shared.Rounding; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -147,10 +148,14 @@ public void UpdateUIState(EntityUid uid, return; var battery = netBat.NetworkBattery; + const int ChargeAccuracy = 5; + + // TODO: Fix ContentHelpers or make a new one coz this is cooked. + var charge = ContentHelpers.RoundToNearestLevels(battery.CurrentStorage / battery.Capacity, 1.0, 100 / ChargeAccuracy) / 100f * ChargeAccuracy; var state = new ApcBoundInterfaceState(apc.MainBreakerEnabled, apc.HasAccess, (int) MathF.Ceiling(battery.CurrentSupply), apc.LastExternalState, - battery.CurrentStorage / battery.Capacity); + charge); _ui.SetUiState((uid, ui), ApcUiKey.Key, state); } diff --git a/Content.Shared/APC/SharedApc.cs b/Content.Shared/APC/SharedApc.cs index 16cd840b3a4..bf9fdc9444b 100644 --- a/Content.Shared/APC/SharedApc.cs +++ b/Content.Shared/APC/SharedApc.cs @@ -65,7 +65,7 @@ public enum ApcLockState : sbyte /// Bitmask for the full state for a given APC lock indicator. /// All = (Lock), - + /// /// The log 2 width in bits of the bitfields indicating the status of an APC lock indicator. /// Used for bit shifting operations (Mask for the state for indicator i is (All << (i << LogWidth))). @@ -175,7 +175,7 @@ public enum ApcChargeState : sbyte } [Serializable, NetSerializable] - public sealed class ApcBoundInterfaceState : BoundUserInterfaceState + public sealed class ApcBoundInterfaceState : BoundUserInterfaceState, IEquatable { public readonly bool MainBreaker; public readonly bool HasAccess; @@ -191,6 +191,27 @@ public ApcBoundInterfaceState(bool mainBreaker, bool hasAccess, int power, ApcEx ApcExternalPower = apcExternalPower; Charge = charge; } + + public bool Equals(ApcBoundInterfaceState? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return MainBreaker == other.MainBreaker && + HasAccess == other.HasAccess && + Power == other.Power && + ApcExternalPower == other.ApcExternalPower && + MathHelper.CloseTo(Charge, other.Charge); + } + + public override bool Equals(object? obj) + { + return ReferenceEquals(this, obj) || obj is ApcBoundInterfaceState other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(MainBreaker, HasAccess, Power, (int) ApcExternalPower, Charge); + } } [Serializable, NetSerializable] @@ -198,7 +219,7 @@ public sealed class ApcToggleMainBreakerMessage : BoundUserInterfaceMessage { } - public enum ApcExternalPowerState + public enum ApcExternalPowerState : byte { None, Low, @@ -206,7 +227,7 @@ public enum ApcExternalPowerState } [NetSerializable, Serializable] - public enum ApcUiKey + public enum ApcUiKey : byte { Key, } From 8530bcf6e878c995c8dd739400b07e00c2b57c87 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Mon, 13 May 2024 17:11:49 +1200 Subject: [PATCH 143/215] Change some `EntityQueryEnumerator` to `AllEntityQuery` (#27969) --- Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs | 4 ++-- Content.Server/Decals/DecalSystem.cs | 2 +- .../Disposal/Unit/EntitySystems/DisposalUnitSystem.cs | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs index 89b9c520787..c714acb2f38 100644 --- a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -122,7 +122,7 @@ private void OnPvsToggle(bool value) } // PVS was turned off, ensure data gets sent to all clients. - var query = EntityQueryEnumerator(); + var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var grid, out var meta)) { grid.ForceTick = _gameTiming.CurTick; @@ -269,7 +269,7 @@ private bool UpdateChunkTile(GridAtmosphereComponent gridAtmosphere, GasOverlayC private void UpdateOverlayData() { // TODO parallelize? - var query = EntityQueryEnumerator(); + var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var overlay, out var gam, out var meta)) { var changed = false; diff --git a/Content.Server/Decals/DecalSystem.cs b/Content.Server/Decals/DecalSystem.cs index c8e062ce6ff..519f7a133e5 100644 --- a/Content.Server/Decals/DecalSystem.cs +++ b/Content.Server/Decals/DecalSystem.cs @@ -89,7 +89,7 @@ private void OnPvsToggle(bool value) playerData.Clear(); } - var query = EntityQueryEnumerator(); + var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var grid, out var meta)) { grid.ForceTick = _timing.CurTick; diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 7f4ee87ee11..1f3b9703d4c 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -209,10 +209,11 @@ public override void Update(float frameTime) { base.Update(frameTime); - var query = EntityQueryEnumerator(); + var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var unit, out var metadata)) { - Update(uid, unit, metadata, frameTime); + if (!metadata.EntityPaused) + Update(uid, unit, metadata, frameTime); } } From 65e8d85481c6ff40c32fbde28c2b13424b5e2d91 Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Mon, 13 May 2024 21:25:58 +0200 Subject: [PATCH 144/215] Fix skirts femalemask (#27984) Fix skirts --- .../Clothing/Uniforms/color_jumpskirts.yml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/color_jumpskirts.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/color_jumpskirts.yml index 1f77059841b..a2bf6b687a9 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/color_jumpskirts.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/color_jumpskirts.yml @@ -1,6 +1,6 @@ # White Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorWhite name: white jumpskirt description: A generic white jumpskirt with no rank markings. @@ -27,7 +27,7 @@ # Grey Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorGrey name: grey jumpskirt description: A tasteful grey jumpskirt that reminds you of the good old days. @@ -58,7 +58,7 @@ # Black Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorBlack name: black jumpskirt description: A generic black jumpskirt with no rank markings. @@ -89,7 +89,7 @@ # Blue Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorBlue name: blue jumpskirt description: A generic blue jumpskirt with no rank markings. @@ -120,7 +120,7 @@ # Dark Blue Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorDarkBlue name: dark blue jumpskirt description: A generic dark blue jumpskirt with no rank markings. @@ -151,7 +151,7 @@ # Teal Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorTeal name: teal jumpskirt description: A generic teal jumpskirt with no rank markings. @@ -182,7 +182,7 @@ # Green Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorGreen name: green jumpskirt description: A generic green jumpskirt with no rank markings. @@ -213,7 +213,7 @@ # Dark Green Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorDarkGreen name: dark green jumpskirt description: A generic dark green jumpskirt with no rank markings. @@ -244,7 +244,7 @@ # Orange Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorOrange name: orange jumpskirt description: Don't wear this near paranoid security officers. @@ -275,7 +275,7 @@ # Pink Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorPink name: pink jumpskirt description: Just looking at this makes you feel fabulous. @@ -306,7 +306,7 @@ # Red Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorRed name: red jumpskirt description: A generic red jumpskirt with no rank markings. @@ -337,7 +337,7 @@ # Yellow Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorYellow name: yellow jumpskirt description: A generic yellow jumpskirt with no rank markings. @@ -368,7 +368,7 @@ # Purple Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorPurple name: purple jumpskirt description: A generic light purple jumpskirt with no rank markings. @@ -399,7 +399,7 @@ # Light Brown Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorLightBrown name: light brown jumpskirt description: A generic light brown jumpskirt with no rank markings. @@ -430,7 +430,7 @@ # Brown Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorBrown name: brown jumpskirt description: A generic brown jumpskirt with no rank markings. @@ -461,7 +461,7 @@ # Maroon Jumpskirt - type: entity - parent: ClothingUniformBase + parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtColorMaroon name: maroon jumpskirt description: A generic maroon jumpskirt with no rank markings. From 6619566610aa6ef0fa010f8c2fc9d55b4dbcc9fb Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 13 May 2024 19:27:06 +0000 Subject: [PATCH 145/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index a979fa6f89a..a4a529dc07f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Brandon-Huu - changes: - - message: Removed the ability for command or any antag-safe role to be initial - infected in zombie mode - type: Remove - id: 6090 - time: '2024-03-05T04:34:18.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25785 - author: Whisper changes: - message: Glass shards can be added to flatcaps to create bladed flatcaps! @@ -3870,3 +3862,10 @@ id: 6589 time: '2024-05-13T00:03:07.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27962 +- author: lzk228 + changes: + - message: Fixed color jumpskirts female mask. + type: Fix + id: 6590 + time: '2024-05-13T19:25:58.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27984 From 6a83e2bb1b66fb829035a487d2fed89a357ffeb3 Mon Sep 17 00:00:00 2001 From: Brandon Li <48413902+aspiringLich@users.noreply.github.com> Date: Mon, 13 May 2024 17:46:16 -0400 Subject: [PATCH 146/215] Reagent dispenser UI (Again) (#27958) * reagent dispenser: fancy window * reagent dispenser: dispense button grid * reagent dispenser: rearrange containers & info * reagent dispenser: remove `reagent-dispenser-window-container-label` * reagent dispenser: add `Scrollcontainer` on right side * reagent dispenser: get rid of pointless actions * reagent dispenser: cleanup actions and `inventory` field on bound ui state * reagent dispenser: cool reagent cards & finishing touches * reagent dispenser: final cleanup and formatting * reagent dispenser: `ButtonGrid` and `ReagentDispenserSetDispenseAmountMessage` refactor * reagent dispenser: cleanup code & address minor concerns * reagent dispenser: text in reagent cards no longer clips * reagent dispenser: oh wait i forgot to change this and thats why the builds keep failing probably * reagent dispenser mayybe this * reagent dispenser: remove `using FastAccessors;` * delete unused classes * disable reagent button when container is empty * Make things a bit bigger * remove obsolete text color override --- .../Chemistry/UI/ReagentCardControl.xaml | 7 +++--- .../Chemistry/UI/ReagentCardControl.xaml.cs | 6 +++-- .../Chemistry/UI/ReagentDispenserWindow.xaml | 3 ++- .../UI/ReagentDispenserWindow.xaml.cs | 24 ------------------- .../EntitySystems/ReagentDispenserSystem.cs | 3 +-- .../Chemistry/SharedReagentDispenser.cs | 5 ++-- 6 files changed, 13 insertions(+), 35 deletions(-) diff --git a/Content.Client/Chemistry/UI/ReagentCardControl.xaml b/Content.Client/Chemistry/UI/ReagentCardControl.xaml index 966c7301406..257b0b6c52e 100644 --- a/Content.Client/Chemistry/UI/ReagentCardControl.xaml +++ b/Content.Client/Chemistry/UI/ReagentCardControl.xaml @@ -1,7 +1,7 @@ - + + HorizontalExpand="True"> - public void SetIgnited(Entity ent, bool ignited = true) { - if (!Resolve(ent, ref ent.Comp)) + if (!Resolve(ent, ref ent.Comp, false)) return; ent.Comp.Ignited = ignited; From 7648caa48950f136d96e0920f1029e5ffae60a5f Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 13 May 2024 21:50:50 +0000 Subject: [PATCH 149/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index de5d1cb9c33..c08d1b6c8f4 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: MACMAN2003 - changes: - - message: Added clockwork structures and brass to build them with. - type: Add - id: 6092 - time: '2024-03-06T00:25:59.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/24673 - author: deepdarkdepths changes: - message: Added the paramedic's cap to the uniform printer. @@ -3869,3 +3862,10 @@ id: 6591 time: '2024-05-13T21:46:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27958 +- author: deltanedas + changes: + - message: Fixed fire spreading being insane. + type: Fix + id: 6592 + time: '2024-05-13T21:49:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27986 From 3fb52f7c14dd2ede60cd799a21d7937698ed0000 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Mon, 13 May 2024 20:29:50 -0400 Subject: [PATCH 150/215] Revert "Stop Toilets crushing you into walls" (#27994) Revert "Stop Toilets crushing you into walls (#27778)" This reverts commit 24e227660a34e33966f5b9bd7a5f69c775c9669b. --- Resources/Prototypes/Entities/Structures/Furniture/toilet.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index a3124c09f6a..2556b9ddfde 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -51,10 +51,6 @@ disposals: !type:Container - type: Physics bodyType: Static - - type: Fixtures - fixtures: - fix1: - hard: false - type: Construction graph: Toilet node: toilet From 5a8675f3753a7f05daededcb8f282ec84c30904e Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Mon, 13 May 2024 21:03:06 -0700 Subject: [PATCH 151/215] Fix profile editor save buttons disappearing at small resolutions (#28001) --- Content.Client/Lobby/UI/HumanoidProfileEditor.xaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml index 918b6840b45..03a205e94a8 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml @@ -7,7 +7,7 @@ - + @@ -31,13 +31,13 @@ - - - + + + + /// Left-side of the station we're allowed to use @@ -94,6 +88,13 @@ private void InitializeFTL() _physicsQuery = GetEntityQuery(); _statusQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); + + _cfg.OnValueChanged(CCVars.FTLStartupTime, time => DefaultStartupTime = time, true); + _cfg.OnValueChanged(CCVars.FTLTravelTime, time => DefaultTravelTime = time, true); + _cfg.OnValueChanged(CCVars.FTLArrivalTime, time => DefaultArrivalTime = time, true); + _cfg.OnValueChanged(CCVars.FTLCooldown, time => FTLCooldown = time, true); + _cfg.OnValueChanged(CCVars.FTLMassLimit, time => FTLMassLimit = time, true); + _cfg.OnValueChanged(CCVars.HyperspaceKnockdownTime, time => _hyperspaceKnockdownTime = TimeSpan.FromSeconds(time), true); } private void OnStationPostInit(ref StationPostInitEvent ev) @@ -215,7 +216,9 @@ public bool CanFTL(EntityUid shuttleUid, [NotNullWhen(false)] out string? reason return false; } - if (TryComp(shuttleUid, out PhysicsComponent? shuttlePhysics) && shuttlePhysics.Mass > FTLMassLimit) + if (FTLMassLimit > 0 && + TryComp(shuttleUid, out PhysicsComponent? shuttlePhysics) && + shuttlePhysics.Mass > FTLMassLimit) { reason = Loc.GetString("shuttle-console-mass"); return false; @@ -248,15 +251,18 @@ public void FTLToCoordinates( ShuttleComponent component, EntityCoordinates coordinates, Angle angle, - float startupTime = DefaultStartupTime, - float hyperspaceTime = DefaultTravelTime, + float? startupTime = null, + float? hyperspaceTime = null, string? priorityTag = null) { if (!TrySetupFTL(shuttleUid, component, out var hyperspace)) return; - hyperspace.StartupTime = startupTime; - hyperspace.TravelTime = hyperspaceTime; + startupTime ??= DefaultStartupTime; + hyperspaceTime ??= DefaultTravelTime; + + hyperspace.StartupTime = startupTime.Value; + hyperspace.TravelTime = hyperspaceTime.Value; hyperspace.StateTime = StartEndTime.FromStartDuration( _gameTiming.CurTime, TimeSpan.FromSeconds(hyperspace.StartupTime)); @@ -280,16 +286,19 @@ public void FTLToDock( EntityUid shuttleUid, ShuttleComponent component, EntityUid target, - float startupTime = DefaultStartupTime, - float hyperspaceTime = DefaultTravelTime, + float? startupTime = null, + float? hyperspaceTime = null, string? priorityTag = null) { if (!TrySetupFTL(shuttleUid, component, out var hyperspace)) return; + startupTime ??= DefaultStartupTime; + hyperspaceTime ??= DefaultTravelTime; + var config = _dockSystem.GetDockingConfig(shuttleUid, target, priorityTag); - hyperspace.StartupTime = startupTime; - hyperspace.TravelTime = hyperspaceTime; + hyperspace.StartupTime = startupTime.Value; + hyperspace.TravelTime = hyperspaceTime.Value; hyperspace.StateTime = StartEndTime.FromStartDuration( _gameTiming.CurTime, TimeSpan.FromSeconds(hyperspace.StartupTime)); diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 5ecac6ec3e0..e1616edd210 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1381,6 +1381,42 @@ public static readonly CVarDef public static readonly CVarDef PreloadGrids = CVarDef.Create("shuttle.preload_grids", true, CVar.SERVERONLY); + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef FTLStartupTime = + CVarDef.Create("shuttle.startup_time", 5.5f, CVar.SERVERONLY); + + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef FTLTravelTime = + CVarDef.Create("shuttle.travel_time", 20f, CVar.SERVERONLY); + + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef FTLArrivalTime = + CVarDef.Create("shuttle.arrival_time", 5f, CVar.SERVERONLY); + + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef FTLCooldown = + CVarDef.Create("shuttle.cooldown", 10f, CVar.SERVERONLY); + + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef FTLMassLimit = + CVarDef.Create("shuttle.mass_limit", 300f, CVar.SERVERONLY); + + /// + /// Whether to automatically spawn escape shuttles. + /// + public static readonly CVarDef HyperspaceKnockdownTime = + CVarDef.Create("shuttle.hyperspace_knockdown_time", 5f, CVar.SERVERONLY); + /* * Emergency */ diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 620de422532..0decb9a11b2 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -55,6 +55,7 @@ AL BB CC + FTL GC GD GL From 84dc0e5285f727b9064fed438f51a0fd36d4a8e2 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 11 May 2024 01:20:48 -0700 Subject: [PATCH 110/215] Fix const data field in BlindableComponent (#27919) * Fix const data field in BlindableComponent * Fix usages --- Content.Shared/Eye/Blinding/Components/BlindableComponent.cs | 2 +- Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs | 4 ++-- Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs b/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs index 39718bd4cf5..c2e73bd6b89 100644 --- a/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs +++ b/Content.Shared/Eye/Blinding/Components/BlindableComponent.cs @@ -25,7 +25,7 @@ public sealed partial class BlindableComponent : Component public int EyeDamage = 0; [ViewVariables(VVAccess.ReadOnly), DataField] - public const int MaxDamage = 9; + public int MaxDamage = 9; [ViewVariables(VVAccess.ReadOnly), DataField] public int MinDamage = 0; diff --git a/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs b/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs index 0232bcf9376..24eed3adcf5 100644 --- a/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs @@ -37,7 +37,7 @@ public void UpdateIsBlind(Entity blindable) var old = blindable.Comp.IsBlind; // Don't bother raising an event if the eye is too damaged. - if (blindable.Comp.EyeDamage >= BlindableComponent.MaxDamage) + if (blindable.Comp.EyeDamage >= blindable.Comp.MaxDamage) { blindable.Comp.IsBlind = true; } @@ -70,7 +70,7 @@ private void UpdateEyeDamage(Entity blindable, bool isDamag return; var previousDamage = blindable.Comp.EyeDamage; - blindable.Comp.EyeDamage = Math.Clamp(blindable.Comp.EyeDamage, blindable.Comp.MinDamage, BlindableComponent.MaxDamage); + blindable.Comp.EyeDamage = Math.Clamp(blindable.Comp.EyeDamage, blindable.Comp.MinDamage, blindable.Comp.MaxDamage); Dirty(blindable); if (!isDamageChanged && previousDamage == blindable.Comp.EyeDamage) return; diff --git a/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs b/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs index 5326af9636e..9a4afaec3a9 100644 --- a/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/EyeClosingSystem.cs @@ -1,4 +1,3 @@ - using Content.Shared.Actions; using Content.Shared.Eye.Blinding.Components; using Robust.Shared.Audio.Systems; @@ -124,7 +123,7 @@ public void UpdateEyesClosable(Entity blindable) if (_entityManager.TryGetComponent(blindable, out var eyelids) && !eyelids.NaturallyCreated) return; - if (ev.Blur < BlurryVisionComponent.MaxMagnitude || ev.Blur >= BlindableComponent.MaxDamage) + if (ev.Blur < BlurryVisionComponent.MaxMagnitude || ev.Blur >= blindable.Comp.MaxDamage) { RemCompDeferred(blindable); return; From 1548f9b9ade3e577e27643166ea0111e74ddf720 Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Sat, 11 May 2024 02:25:40 -0700 Subject: [PATCH 111/215] Fix collection modified error when locking storage (#27913) --- Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 9f834578188..e6a1d176976 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -1415,11 +1415,11 @@ private void OnLockToggled(EntityUid uid, StorageComponent component, ref LockTo return; // Gets everyone looking at the UI - foreach (var actor in _ui.GetActors(uid, StorageComponent.StorageUiKey.Key)) + foreach (var actor in _ui.GetActors(uid, StorageComponent.StorageUiKey.Key).ToList()) { if (_admin.HasAdminFlag(actor, AdminFlags.Admin)) continue; - + // And closes it unless they're an admin _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, actor); } From b4a765bc00ba200706c69e4169e937c0d3b3904d Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Date: Sat, 11 May 2024 05:11:20 -0700 Subject: [PATCH 112/215] Fix shuttle cvars comments (#27923) * Fix shuttle cvars comments * Add another line to ftl mass limit --- Content.Shared/CCVar/CCVars.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index e1616edd210..5b8f27914db 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1,6 +1,7 @@ using Content.Shared.Maps; using Robust.Shared; using Robust.Shared.Configuration; +using Robust.Shared.Physics.Components; namespace Content.Shared.CCVar { @@ -1382,37 +1383,38 @@ public static readonly CVarDef CVarDef.Create("shuttle.preload_grids", true, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// How long the warmup time before FTL start should be. /// public static readonly CVarDef FTLStartupTime = CVarDef.Create("shuttle.startup_time", 5.5f, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// How long a shuttle spends in FTL. /// public static readonly CVarDef FTLTravelTime = CVarDef.Create("shuttle.travel_time", 20f, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// How long the final stage of FTL before arrival should be. /// public static readonly CVarDef FTLArrivalTime = CVarDef.Create("shuttle.arrival_time", 5f, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// How much time needs to pass before a shuttle can FTL again. /// public static readonly CVarDef FTLCooldown = CVarDef.Create("shuttle.cooldown", 10f, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// The maximum a grid can have before it becomes unable to FTL. + /// Any value equal to or less than zero will disable this check. /// public static readonly CVarDef FTLMassLimit = CVarDef.Create("shuttle.mass_limit", 300f, CVar.SERVERONLY); /// - /// Whether to automatically spawn escape shuttles. + /// How long to knock down entities for if they aren't buckled when FTL starts and stops. /// public static readonly CVarDef HyperspaceKnockdownTime = CVarDef.Create("shuttle.hyperspace_knockdown_time", 5f, CVar.SERVERONLY); From 2a5d68d86780a376216d57fd7f1054cf4891d5f1 Mon Sep 17 00:00:00 2001 From: "Mr. 27" <45323883+Dutch-VanDerLinde@users.noreply.github.com> Date: Sat, 11 May 2024 09:52:57 -0400 Subject: [PATCH 113/215] Fix security jumpsuit sprite's asymmetry (#27925) * inital * Update meta.json --- .../security.rsi/equipped-INNERCLOTHING.png | Bin 1266 -> 663 bytes .../Uniforms/Jumpsuit/security.rsi/meta.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/equipped-INNERCLOTHING.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/equipped-INNERCLOTHING.png index b3977c974892c805bea08700ae201a943f3ce16e..2fb771d84c3133abcc7c0cfd62cb6fe93f5016bb 100644 GIT binary patch delta 650 zcmV;50(JfJ36}+s8Gi!+005o0f$RVP00DDSM?wIu&K&6g000DMK}|sb0I`n?{9y$E z0016POjJdEC@fqbBs~`#!7VVKDlRWBF(V=-R8vqG7a0Hm0F-c3UpFVQ~&?} z|NsC0|Nj88%q-6U000SaNLh0L01m?d01m?e$8V@)0005(NqLSs&?B^5HY1|o=l_+U{_)X&3ftvLctpmn#25i^ z9StGmpxNmc%YUZDy6GOtGuH^d(8-9v9o??eh}X{l00h>L0H#n|M}TR4@gwiQH!L~; zme{B(-SZj{b~T-?*VNMntc?2jC%oyyeFo?jy3fA5wOy_H2WxxATLM2 z2;*`JykRM`ADxZ>TLGZ+mae9djSZgz;P3SC*h0wzYJb2KK<{gSHGm7~^d2w@==2_d zZkMr!{vPm=PQVF}PQVcmEVb5D=}Kh? z0XX!PJ%66+0g=4<4FF-ymK=c8sYPB+L0SV`5$8qmP&EZq&GUkC!Y!uRl3xQv2`~px z9Vli75J4HfSp$*RAoS0BK%KK~#90?V3$XBS{#?|LMF?F=3cdM_~~f z8v`14P@}J$1uwh79uST!q_{ji7$zb znyx*>YAZd_bX7X-E>pjYrfuv0)Km35RjGOag+ifFC=?3Cw|_}Y`n%>w-A~=y%-EG#GJBSU=E^OK&$Ux_d$$v!XPT)JwYcY76jJSrMVljOBqaH|K8}-84YJui*I!c``4zC=TJr@9A z#5H8I8P6cI=X!rHCKK3NUZH~h9EyMB8HT0}`~(2gby4bck;~~=TP+0APmFrunf(Rz z%MP;H3@rN#Do%gumFH%?ci#W~&%F`p`xy}ytn;;28-K=KxG@cW0BWr^vhy!duQ%{r zryHp4M=jl2UZLf=S-eZ7VB5z4fVVH6qck%;@Lp+V8gE}b2LRMsZ7&qYodw?L-*q0q zz%1Zl&u#k{?@}q0=Vk-xj~uGKn_JXcZ5-R5y_oWkF#mbqK8i6Xj0+2?TB|*<>O5oz z036$&g@5!P4FR^~$-qWzTq-B0Up?cKA^SW`UngW_`;R(+Uu#7eHd-<~q;I-D2l&E* z>F)>7kjhqmj}IGxNy9Y9)fO75_4x$~VCd9hp%T{UN2Nb<8T7+vd;ZyCnuHejLG=CW zzpTDg2pF2??E}hlvjfIN0o(SnTwrLLv<+if9)BduhE1*2?suNsc^YwT=xPxJX$S0_ zUt-rfi0zzT$~GYd+k{m+UF=#1vFhE9uQW3)H6U^!@W@7Jl1yU830XeMfTswFaSmKglzHH(^%V+*LZMJ76bgmn+huEcg>LFg^ke>YC>NmP@G<|I zZhz`aRQ4K9_z0V?6WBSw^zP<#; z@8A7CVtDP zF)ikZ-{Tqzg;#wW>kXl{gwC-Fg;!`cEq~POjefm=jm#9-?3g4UQb}xN zrcg{KP&qk;runHTuFSRqQxp4-0{te6 zFzPY#XBXJH`#p?M6n23B`irf{!2PCZ(y+~snI;1jqkyK|>H@*Lq1VCaN+UqYVhZjM!{?3`ap z=Y#*}m2QYgJ0PkrK0z4A7HJ3km+Aq=u|+Be=!6Wtr;i#w_vnNKsti>q6a)MVGVDot T-(-B;00000NkvXXu0mjf-@sgV diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/meta.json b/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/meta.json index 3f72fb44600..c11c48fe747 100644 --- a/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/meta.json +++ b/Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039. In hand sprite scaled down by potato1234_x, monkey made by brainfood1183 (github) for ss14", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039. In hand sprite scaled down by potato1234_x, monkey made by brainfood1183 (github) for ss14. equipped-INNERCLOTHING edited by Dutch-VanDerLinde (github).", "size": { "x": 32, "y": 32 From 776af6a2f2f166ee3777bb7047c9633bf4aef501 Mon Sep 17 00:00:00 2001 From: RiceMar1244 <138547931+RiceMar1244@users.noreply.github.com> Date: Sat, 11 May 2024 10:40:01 -0400 Subject: [PATCH 114/215] Fixes oversight in wieldable smg accuracy (#27907) --- .../Entities/Objects/Weapons/Guns/SMGs/smgs.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml index 8a679dcbec1..8e02eac5f7b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml @@ -80,7 +80,7 @@ - type: entity name: C-20r sub machine gun - parent: [BaseWeaponSubMachineGun, BaseGunWieldable] + parent: BaseWeaponSubMachineGun id: WeaponSubMachineGunC20r description: A firearm that is often used by the infamous nuclear operatives. Uses .35 auto ammo. components: @@ -93,9 +93,13 @@ map: ["enum.GunVisualLayers.Mag"] - type: Clothing sprite: Objects/Weapons/Guns/SMGs/c20r.rsi + - type: Wieldable + - type: GunWieldBonus + minAngle: -19 + maxAngle: -16 - type: Gun minAngle: 21 - maxAngle: 32 + maxAngle: 32 shotsPerBurst: 5 availableModes: - SemiAuto @@ -113,7 +117,7 @@ - type: entity name: Drozd - parent: [BaseWeaponSubMachineGun, BaseGunWieldable] + parent: BaseWeaponSubMachineGun id: WeaponSubMachineGunDrozd description: An excellent fully automatic Heavy SMG. components: @@ -126,9 +130,13 @@ map: ["enum.GunVisualLayers.Mag"] - type: Clothing sprite: Objects/Weapons/Guns/SMGs/drozd.rsi + - type: Wieldable + - type: GunWieldBonus + minAngle: -19 + maxAngle: -16 - type: Gun minAngle: 21 - maxAngle: 32 + maxAngle: 32 fireRate: 6 selectedMode: FullAuto soundGunshot: From f51dc9fdfbf743fbb505e0045046165fe2c3d5f4 Mon Sep 17 00:00:00 2001 From: Flareguy <78941145+Flareguy@users.noreply.github.com> Date: Sat, 11 May 2024 09:54:56 -0500 Subject: [PATCH 115/215] Gives vox The Brows in their front facing sprite (#27922) * gives vox The Brows in their front facing sprite * JASON --- .../Mobs/Species/Vox/parts.rsi/full.png | Bin 789 -> 493 bytes .../Mobs/Species/Vox/parts.rsi/head.png | Bin 689 -> 429 bytes .../Mobs/Species/Vox/parts.rsi/meta.json | 2 +- .../Mobs/Species/Vox/parts.rsi/vox_m.png | Bin 2545 -> 1123 bytes 4 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Textures/Mobs/Species/Vox/parts.rsi/full.png b/Resources/Textures/Mobs/Species/Vox/parts.rsi/full.png index 864a6f00376be96bbf8ad96a633fc2cd946d03c1..6338e4d1128571aba60319383e188837560825cd 100644 GIT binary patch delta 478 zcmbQr_Lg~qWIZzj1B1(wu46!ou{g-xiDBJ2nU_EgOS+@4BLl<6e(pbstU$g^fKQ04 zXP|Istawg|tas%^@xQ+-QtT(!*~X+wM0wc;MTt4O3whhxdD+@}*w|R=0gxv+2Ib1yMw4hPKKf|d%9rzXUY^^mlIHhGJHUhSMdoXsdKLk%h2g&34ZpcHIn!QR zubVkJlB3CB^VhBW9>ttk)ZwBW*}>V7qh{!~(RZ4o+cx8dXB#-**Os$R5{uyA=~_1b zz_~7_+9T-~G7T9d9>@tkJh0`(fq4%aZdY>)oN9h?Ao^{Bn4sO`Hy0bf_utva(x)19 z@+VV`QbE#>{Rv^KN-u{qOb9D~|9H0efv5Kv_h-ya&NyjWbZnC4qeITz6C$-2O#OZB z)wE*PSH&5uKUN$#xHcz>;TqoyFP{fX*DqFM-!#i%X$|8QM;rI-j>JAtD0{m4xvX|27rl58 zK}$i}z+z)7)V8rro_=1O^(F3h-`j&;99Ujv-pqWzJ8#}>M1TIvKdwMj1poS-IWrme z^l`6Hu2Lugz-MNTNwZlKcTFSZlRJpDg4gTEC62!*`BmT9Ih4>!1XsWXZ&uG+5|F`O zM1l#dRqo1*2kWBXtmxQ_NTDMD2;j4gp=@+Zvf6)dvH!{uRHvc@98gF=273_+IszK3 zw|O9+y2CLnbbsuj4M>t80t9gNwyN)>^LoUA6&-sK@vfWRbATu}Z#>&yvfy*`73uFA z+XF~QCA98i5+GC1N-oR8!Z zAPK`}xUnG5pM8n8dWX{O_Q!IgV`m>f{C(v}&zr5w=Gs+b?|u8pOWS=@waE5@E9lq} z{O~7^_&g%HRMXyuYo}#rMV}PkDr6FX{l&$|j#?Eu8?fDPLwo)RD>`{n6;#~4sN0CWjXpFR1jI6DXLMP{eHX^IufBq$D6^LuE z=d&3V&!azI?plV(px4uP(`>hq0{;D>`=e z@e2`e0yGsM`afV21UviMw7w*~K1L+#yk!!$GAA3rJR?7U@vZ+8Gi!+005o0f$RVP00DDSM?wIu&K&6g000DMK}|sb0I`n?{9y$E z000lr3`}?r4u#S$70001FEo-j;000PdQchF<|NsC0|Nj8q zgS}Y*000SaNLh0L01FWS01FWTe`H^g0003BNklb z32L@1OSXjLMJvit+Y2n$hy0qL2==~WF+Oz*G1}fxsG_+w?I}n;9Sq2?T%BPS=oa?^ zMn2kJ!0=a|Cp=Lw2I^iwm{JcrATgYdpewX_g(pNWZ7aA+hoJBBF^(;q{(>3~`C2BR01_q{N zPZ!6KinzD4V*4B&1zM}~be#4GaDO<^>{^keDVC&JH|=4gDhK}&IS%9HlP7PnW(&aOXRKMEq`t`ftPp##1RAc%->p;8u?~fHVdcS$un8VlDFYkXM-hEVZTHbock1FNo z+kf4DYxb)6^;P|MoXf5&2CcvDy!$TS>8D0tj7Sb&!zW@b*MCt|Kua`SqW_EQC7*c)HjHO= z{j23WnOV+PN+o=~`TqOG)|1Kd%8k$0emoP*@LBh5slx%o=S#OTUi-ap@;8g+2UmZ2 z+?;1M_tTbsZx0mupPAs;Ac9vi@(x){{a%i=HXzrvA;VXAN=eW=<_@>KM6yC!WP8=M8!!JxDl;^0FkrMj7?)ijJ?o oH}8ud{N7i@GLTxA$bd65_W`*>tOlYTu~NpIkV!;AR9HvNR@)YvAQ0q|07`9%{{O#rX5Brdh?yxKfn&ThwzX=#Z%Oec88j_D}&4$Tbx>OoBj~`GZFY-)7;k zP)NpV?G7Xub2JqI?M*W1VjP1pep$R=@=fdu~RiTo4|lou=$ z?-ulQSD?Q`_xLpmmw*Q$)3mI9FcBgE5;kLj9H}f3tbaacZM~4AM&KY&@s~hI2SPxI z>yg4hfV@P|@^6706Dk;oA_*x+DVz9@vdP7lu zCJ>Vvq_M)7zXZ0l?NZoLl0u7=cIEd}Ww9Q@Uw`306u>KICnp2nq`SR7-!O7~jwG<> zle;{o2_QfozuM4&$2$f&5LgMO8VUF^ z1{nd;zzI^9Q()x>F1#?=%;L8Qm_sfR7OkZn1XkRfeh9=B24><=_;&(0b(`;(8yNlk0|UJmxu+NyGyR%AwslP76gU7RGTF$6Y-Ris8V?73u>jlRQ(6~oX7qH{Rb*V z;w6=c8YP6jAg~%M5vkOCpytEt6>Hqi#s;Gr8)H0oABHo&;}7s1+o`mhpA?Z9=HA~q z=bn4cxgP*;@fQErU``s%&V_jWyQRgPqrU!Ze(6TDb0O~cyAg?;g03G}KMRG!H(~_h zSG)}6eQy19%I|VQPD%r?jNtdXn~qF3Au105k;o|kfZ}D2`K1g%q@;XQ83j-?!kYl` z1O`0F?^X~P@YvqVLfQZ+&(~P{E^R3vRYtM)T^cFR*Ny~`g{ZXnGrwDL)St8g)bux) z{^BeEU{Esv0MlQbg_{1xkpQwN@AC#_^Sc$4_npl?X#*%;hNv=XDL<$gh$^GjyzDg; zFSCs}d4yAbDFZAm=2$LU!Dhy=z2D3j$Ym=%&jQ3xF^Hh+2afrrJ;3jGV@KIQV%>aB ztOGmB2K;`vY*&s^C>&ZYPbttT+O`;_7)nFAYz4vc70vqKAfEk@&5ik06<@#*`&S@sa9)2s8p(U)d;h5As&sY$Yjdc z$sHmXazpjGEJFaW-fNnIWU|;c|A|w8lvjN&luA`ptK3$A0JR)kDpjHSTsnD% zul)Fg&r)9XxhyXj96E)a+#xcVGNMtnZT_|M+`A5{@w)xx20$UjujIDE6S>9&kW+KQvSC+S#v2_kx8R_J5BEL63f|ccM_Uy?X z0s|gv8Wi^oruoDOvr1mTjL4LS#Zt7mXCM^z*dp(sW?*83Irf59{8R7N~J17VGlG-!R#FP(qfLK z(m*N=oE~s&11#JE5|85+KY3q$>02lqf@GVW3-P7J99y^>M=BjVk)x#n9=%N9wO(as zB7K$%QNStg8MZ*!-{04;CV!>hZ)h)rGm$>|4NFRT#n_QIt0oaT{$_EBKRxE1P zz8Ox;7q9iI<$i$yk1Yj?eV4Fyp<{B2hZ!SWaa z10Ia1!^kJcaPx-F0N~~go#m5b7*U7Kd|V!Dw@WDm%VT)-<1ns&b+IY`^{+1C(T~H@ z{B0W`l{QOE+6SmdV%_|GDlNs|Hd1L|{u++upT7oa{u+oA)0MGR$9->CGAb*lW(;>itP*+W_BQkMUOSM@X!j-#L<` z4gTXs9{}DzE6?Aq0r*ix2B6W+=y~C8oaSg(jWB=3G(L@TEB-(Bm>CPM^kxv=;w}CM z(Q(QWx627sq;wmfejc&rcDJ_RJ8Dcjwj#uHx^5YQ6sKb`5i(og&EU8pDc|Z;me%Q= z8sXE=BRmp0bu3ROM(FB!mbfiezeVO0g~DOO`acwIT=Y0aN87hFD!FV0xoib~zZ-hQm-_6dm;~i~`UX4w2+TQ;8``_Ujy$-+MZSjdRI7P=XAQUz?A!t-cuxM0;KmP#+ zH3Ron81BT^q_}8Dcq`*VA@EOY$t&^@cPBBR<0(+y=awLVsW<>^Zw$k)b+$W9`}^A) z!-$Uk3jl0}aC#|1gyShd=}(-WON%*{Ocs$$7O|sjU^8Ph+$E3!wck-TK>doNP28QF zLpt(+`m1W6=JIJi zKH;;1AIab}9ZKI5tXDpltynw4;}bs2Tt3~im6)hJ?EsSp|KJH-40xUw98c^Y(_ zE(DtaW-g!R;lI?B5N=;`YB;DFKxP!bY-L>7|II8> z>7sHps=})>OilJ~J8Eh&#=R;tt4!h2^!{(-*vhzYZ-t?#KDB~%vs>g#i#a9^=!tcJ z;$_hF1FOPIJ*b`fQV^tX-W16F#rlKXUvzuL%TOpB-&ei6^7?mF?^Cqh`Sq`u7%@Lt zeP|$h;iWYvBau@`CX2`?$GUz#Ff|$Df#?RJQPtwg?^cj}a1NLLW;*hd=N!S&7q%4L zq%N@OSf2(Pgr@7MG#!GYFYx3!x4eMX0Afi@0(BE&pTtGn=IGayFBA@sTO_s40<9nh zkU{}ifAJHxcQ4|Vejgu216C12>1p+$^ZLIPRv#J&MA5+YO23bVTNm+4{|O(312%cv zf%Ii*>iq2a7uNgw`$Dhu`!e0O1?sb&OO!>FuU$CtRsQ$Ue*ggJ;sFUnqjKDZ1gAKO z>qw^F{mBfaw@)rj;~gKz%^NyC`|@2xqbhRQh6|Ius0A2M^MG8|oC=?P`7UnW(D9Cs z$M{{#gc&3^1g*Y=Nf! zx!0JQjB(N3>z{jy`71Ggq%1!=hi`c9A$?2EsmU03axdV&bmRruP=F!s00000NkvXX Hu0mjf1AW{Q From 411ffc9b9df13f7c487650f3f541f438225c1237 Mon Sep 17 00:00:00 2001 From: Vasilis Date: Sat, 11 May 2024 18:01:28 +0300 Subject: [PATCH 116/215] Do not wake up NPC if there is still a mind attached. (#27651) * Do not wake up NPC if there is still a mind attached. This became apparent with diona nymphs (?) and slime gyras (?). This caused players that disconnected while a nymph, gyras or other npc to resume their NPC behavior. Which I would call unwanted. This fixes that. * Zombies become AI anyway * Update Content.Server/NPC/Systems/NPCSystem.cs --------- Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --- Content.Server/NPC/Systems/NPCSystem.cs | 6 ++++++ Content.Server/Zombies/ZombieSystem.Transform.cs | 10 ++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Content.Server/NPC/Systems/NPCSystem.cs b/Content.Server/NPC/Systems/NPCSystem.cs index 91086294350..bc21916976e 100644 --- a/Content.Server/NPC/Systems/NPCSystem.cs +++ b/Content.Server/NPC/Systems/NPCSystem.cs @@ -2,6 +2,8 @@ using Content.Server.NPC.Components; using Content.Server.NPC.HTN; using Content.Shared.CCVar; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Systems; using Content.Shared.NPC; @@ -49,6 +51,10 @@ public void OnPlayerNPCDetach(EntityUid uid, HTNComponent component, PlayerDetac if (_mobState.IsIncapacitated(uid) || TerminatingOrDeleted(uid)) return; + // This NPC has an attached mind, so it should not wake up. + if (TryComp(uid, out var mindContainer) && mindContainer.HasMind) + return; + WakeNPC(uid, component); } diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index 9a5933e233d..83c322f7e62 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -81,7 +81,7 @@ private void OnDamageChanged(EntityUid uid, ZombifyOnDeathComponent component, M /// the entity being zombified /// /// - /// ALRIGHT BIG BOY. YOU'VE COME TO THE LAYER OF THE BEAST. THIS IS YOUR WARNING. + /// ALRIGHT BIG BOYS, GIRLS AND ANYONE ELSE. YOU'VE COME TO THE LAYER OF THE BEAST. THIS IS YOUR WARNING. /// This function is the god function for zombie stuff, and it is cursed. I have /// attempted to label everything thouroughly for your sanity. I have attempted to /// rewrite this, but this is how it shall lie eternal. Turn back now. @@ -241,6 +241,11 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) _identity.QueueIdentityUpdate(target); + var htn = EnsureComp(target); + htn.RootTask = new HTNCompoundTask() { Task = "SimpleHostileCompound" }; + htn.Blackboard.SetValue(NPCBlackboard.Owner, target); + _npc.SleepNPC(target, htn); + //He's gotta have a mind var hasMind = _mind.TryGetMind(target, out var mindId, out _); if (hasMind && _mind.TryGetSession(mindId, out var session)) @@ -256,9 +261,6 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) } else { - var htn = EnsureComp(target); - htn.RootTask = new HTNCompoundTask() { Task = "SimpleHostileCompound" }; - htn.Blackboard.SetValue(NPCBlackboard.Owner, target); _npc.WakeNPC(target, htn); } From ec8d56678363352ab22596befe9d904b097b09f9 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 May 2024 15:02:36 +0000 Subject: [PATCH 117/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 1ee24a441f3..24d0e6436f8 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: SlamBamActionman - changes: - - message: Arachnids are no longer perma-pied and can clean it off. - type: Fix - id: 6083 - time: '2024-03-03T08:54:36.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25713 - author: Dutch-VanDerLinde changes: - message: Monkeys and kobolds can now wear blood-red hardsuits. @@ -3864,3 +3857,11 @@ id: 6582 time: '2024-05-11T02:21:18.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27891 +- author: Vasilis + changes: + - message: NPC's activly being controlled by a player wont return to their AI behevior + if the player disconnects. + type: Fix + id: 6583 + time: '2024-05-11T15:01:29.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27651 From 13fb095cae7a842f5b30bf3a98760587ca603b29 Mon Sep 17 00:00:00 2001 From: ShadowCommander Date: Sat, 11 May 2024 08:03:40 -0700 Subject: [PATCH 118/215] Fix ghosts getting spawned in nullspace (#27617) * Add tests for ghost spawn position * Make ghosts spawn immediately * Format mind system * Move ghost spawning to GhostSystem * Spawn ghost on grid or map This fixes the ghosts being attached the parent entity instead of the grid. * Move logging out of the ghost system * Make round start observer spawn using GhostSystem * Move GameTicker ghost spawning to GhostSystem Moved the more robust character name selection code over. Moved the TimeOfDeath code over. Added canReturn logic. * Add overrides and default for ghost spawn coordinates * Add warning log to ghost spawn fail * Clean up test * Dont spawn ghost on map delete * Minor changes to the role test * Fix role test failing to spawn ghost It was failing the map check due to using Nullspace * Fix ghost tests when running in parallel Not sure what happened, but it seems to be because they were running simultaneously and overwriting values. * Clean up ghost tests * Test that map deletion does not spawn ghosts * Spawn ghost on the next available map * Disallow spawning on deleted maps * Fix map deletion ghost test * Cleanup --- .../Tests/Minds/GhostRoleTests.cs | 12 +- .../Tests/Minds/GhostTests.cs | 159 ++++++++++++++++++ .../Tests/Minds/MindTests.EntityDeletion.cs | 17 +- .../GameTicking/GameTicker.GamePreset.cs | 28 +-- .../GameTicking/GameTicker.Spawning.cs | 40 ++--- Content.Server/Ghost/GhostSystem.cs | 57 +++++++ Content.Server/Mind/MindSystem.cs | 47 +----- 7 files changed, 261 insertions(+), 99 deletions(-) create mode 100644 Content.IntegrationTests/Tests/Minds/GhostTests.cs diff --git a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs index ca97e435a7f..150bc951f8c 100644 --- a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs +++ b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs @@ -1,5 +1,6 @@ #nullable enable using System.Linq; +using Content.IntegrationTests.Pair; using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles.Components; using Content.Server.Players; @@ -26,7 +27,7 @@ public sealed class GhostRoleTests "; /// - /// This is a simple test that just checks if a player can take a ghost roll and then regain control of their + /// This is a simple test that just checks if a player can take a ghost role and then regain control of their /// original entity without encountering errors. /// [Test] @@ -34,12 +35,15 @@ public async Task TakeRoleAndReturn() { await using var pair = await PoolManager.GetServerClient(new PoolSettings { + Dirty = true, DummyTicker = false, Connected = true }); var server = pair.Server; var client = pair.Client; + var mapData = await pair.CreateTestMap(); + var entMan = server.ResolveDependency(); var sPlayerMan = server.ResolveDependency(); var conHost = client.ResolveDependency(); @@ -51,7 +55,7 @@ public async Task TakeRoleAndReturn() EntityUid originalMob = default; await server.WaitPost(() => { - originalMob = entMan.SpawnEntity(null, MapCoordinates.Nullspace); + originalMob = entMan.SpawnEntity(null, mapData.GridCoords); mindSystem.TransferTo(originalMindId, originalMob, true); }); @@ -69,12 +73,12 @@ await server.WaitPost(() => Assert.That(entMan.HasComponent(ghost)); Assert.That(ghost, Is.Not.EqualTo(originalMob)); Assert.That(session.ContentData()?.Mind, Is.EqualTo(originalMindId)); - Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob)); + Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob), $"Original mob: {originalMob}, Ghost: {ghost}"); Assert.That(originalMind.VisitingEntity, Is.EqualTo(ghost)); // Spawn ghost takeover entity. EntityUid ghostRole = default; - await server.WaitPost(() => ghostRole = entMan.SpawnEntity("GhostRoleTestEntity", MapCoordinates.Nullspace)); + await server.WaitPost(() => ghostRole = entMan.SpawnEntity("GhostRoleTestEntity", mapData.GridCoords)); // Take the ghost role await server.WaitPost(() => diff --git a/Content.IntegrationTests/Tests/Minds/GhostTests.cs b/Content.IntegrationTests/Tests/Minds/GhostTests.cs new file mode 100644 index 00000000000..ad9d53a70db --- /dev/null +++ b/Content.IntegrationTests/Tests/Minds/GhostTests.cs @@ -0,0 +1,159 @@ +using System.Numerics; +using Content.IntegrationTests.Pair; +using Content.Shared.Ghost; +using Content.Shared.Mind; +using Content.Shared.Players; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Player; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.Tests.Minds; + +[TestFixture] +public sealed class GhostTests +{ + struct GhostTestData + { + public IEntityManager SEntMan; + public Robust.Server.Player.IPlayerManager SPlayerMan; + public Server.Mind.MindSystem SMindSys; + public SharedTransformSystem STransformSys = default!; + + public TestPair Pair = default!; + + public TestMapData MapData => Pair.TestMap!; + + public RobustIntegrationTest.ServerIntegrationInstance Server => Pair.Server; + public RobustIntegrationTest.ClientIntegrationInstance Client => Pair.Client; + + /// + /// Initial player coordinates. Note that this does not necessarily correspond to the position of the + /// entity. + /// + public NetCoordinates PlayerCoords = default!; + + public NetEntity Player = default!; + public EntityUid SPlayerEnt = default!; + + public ICommonSession ClientSession = default!; + public ICommonSession ServerSession = default!; + + public GhostTestData() + { + } + } + + private async Task SetupData() + { + var data = new GhostTestData(); + + // Client is needed to create a session for the ghost system. Creating a dummy session was too difficult. + data.Pair = await PoolManager.GetServerClient(new PoolSettings + { + DummyTicker = false, + Connected = true, + Dirty = true + }); + + data.SEntMan = data.Pair.Server.ResolveDependency(); + data.SPlayerMan = data.Pair.Server.ResolveDependency(); + data.SMindSys = data.SEntMan.System(); + data.STransformSys = data.SEntMan.System(); + + // Setup map. + await data.Pair.CreateTestMap(); + data.PlayerCoords = data.SEntMan.GetNetCoordinates(data.MapData.GridCoords.Offset(new Vector2(0.5f, 0.5f)).WithEntityId(data.MapData.MapUid, data.STransformSys, data.SEntMan)); + + if (data.Client.Session == null) + Assert.Fail("No player"); + data.ClientSession = data.Client.Session!; + data.ServerSession = data.SPlayerMan.GetSessionById(data.ClientSession.UserId); + + Entity mind = default!; + await data.Pair.Server.WaitPost(() => + { + data.Player = data.SEntMan.GetNetEntity(data.SEntMan.SpawnEntity(null, data.SEntMan.GetCoordinates(data.PlayerCoords))); + mind = data.SMindSys.CreateMind(data.ServerSession.UserId, "DummyPlayerEntity"); + data.SPlayerEnt = data.SEntMan.GetEntity(data.Player); + data.SMindSys.TransferTo(mind, data.SPlayerEnt, mind: mind.Comp); + data.Server.PlayerMan.SetAttachedEntity(data.ServerSession, data.SPlayerEnt); + }); + + await data.Pair.RunTicksSync(5); + + Assert.Multiple(() => + { + Assert.That(data.ServerSession.ContentData()?.Mind, Is.EqualTo(mind.Owner)); + Assert.That(data.ServerSession.AttachedEntity, Is.EqualTo(data.SPlayerEnt)); + Assert.That(data.ServerSession.AttachedEntity, Is.EqualTo(mind.Comp.CurrentEntity), + "Player is not attached to the mind's current entity."); + Assert.That(data.SEntMan.EntityExists(mind.Comp.OwnedEntity), + "The mind's current entity does not exist"); + Assert.That(mind.Comp.VisitingEntity == null || data.SEntMan.EntityExists(mind.Comp.VisitingEntity), + "The minds visited entity does not exist."); + }); + + Assert.That(data.SPlayerEnt, Is.Not.EqualTo(null)); + + return data; + } + + /// + /// Test that a ghost gets created when the player entity is deleted. + /// 1. Delete mob + /// 2. Assert is ghost + /// + [Test] + public async Task TestGridGhostOnDelete() + { + var data = await SetupData(); + + var oldPosition = data.SEntMan.GetComponent(data.SPlayerEnt).Coordinates; + + Assert.That(!data.SEntMan.HasComponent(data.SPlayerEnt), "Player was initially a ghost?"); + + // Delete entity + await data.Server.WaitPost(() => data.SEntMan.DeleteEntity(data.SPlayerEnt)); + await data.Pair.RunTicksSync(5); + + var ghost = data.ServerSession.AttachedEntity!.Value; + Assert.That(data.SEntMan.HasComponent(ghost), "Player did not become a ghost"); + + // Ensure the position is the same + var ghostPosition = data.SEntMan.GetComponent(ghost).Coordinates; + Assert.That(ghostPosition, Is.EqualTo(oldPosition)); + + await data.Pair.CleanReturnAsync(); + } + + /// + /// Test that a ghost gets created when the player entity is queue deleted. + /// 1. Delete mob + /// 2. Assert is ghost + /// + [Test] + public async Task TestGridGhostOnQueueDelete() + { + var data = await SetupData(); + + var oldPosition = data.SEntMan.GetComponent(data.SPlayerEnt).Coordinates; + + Assert.That(!data.SEntMan.HasComponent(data.SPlayerEnt), "Player was initially a ghost?"); + + // Delete entity + await data.Server.WaitPost(() => data.SEntMan.QueueDeleteEntity(data.SPlayerEnt)); + await data.Pair.RunTicksSync(5); + + var ghost = data.ServerSession.AttachedEntity!.Value; + Assert.That(data.SEntMan.HasComponent(ghost), "Player did not become a ghost"); + + // Ensure the position is the same + var ghostPosition = data.SEntMan.GetComponent(ghost).Coordinates; + Assert.That(ghostPosition, Is.EqualTo(oldPosition)); + + await data.Pair.CleanReturnAsync(); + } + +} diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs index 2ebe750f98d..de7739b2ad7 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs @@ -1,3 +1,4 @@ +#nullable enable using System.Linq; using Content.Server.GameTicking; using Content.Shared.Ghost; @@ -77,7 +78,7 @@ public async Task TestGhostOnDeleteMap() await using var pair = await SetupPair(dirty: true); var server = pair.Server; var testMap = await pair.CreateTestMap(); - var coordinates = testMap.GridCoords; + var testMap2 = await pair.CreateTestMap(); var entMan = server.ResolveDependency(); var mapManager = server.ResolveDependency(); @@ -91,7 +92,7 @@ public async Task TestGhostOnDeleteMap() MindComponent mind = default!; await server.WaitAssertion(() => { - playerEnt = entMan.SpawnEntity(null, coordinates); + playerEnt = entMan.SpawnEntity(null, testMap.GridCoords); mindId = player.ContentData()!.Mind!.Value; mind = entMan.GetComponent(mindId); mindSystem.TransferTo(mindId, playerEnt); @@ -100,14 +101,20 @@ await server.WaitAssertion(() => }); await pair.RunTicksSync(5); - await server.WaitPost(() => mapManager.DeleteMap(testMap.MapId)); + await server.WaitAssertion(() => mapManager.DeleteMap(testMap.MapId)); await pair.RunTicksSync(5); await server.WaitAssertion(() => { #pragma warning disable NUnit2045 // Interdependent assertions. - Assert.That(entMan.EntityExists(mind.CurrentEntity), Is.True); - Assert.That(mind.CurrentEntity, Is.Not.EqualTo(playerEnt)); + // Spawn ghost on the second map + var attachedEntity = player.AttachedEntity; + Assert.That(entMan.EntityExists(attachedEntity), Is.True); + Assert.That(attachedEntity, Is.Not.EqualTo(playerEnt)); + Assert.That(entMan.HasComponent(attachedEntity)); + var transform = entMan.GetComponent(attachedEntity.Value); + Assert.That(transform.MapID, Is.Not.EqualTo(MapId.Nullspace)); + Assert.That(transform.MapID, Is.Not.EqualTo(testMap.MapId)); #pragma warning restore NUnit2045 }); diff --git a/Content.Server/GameTicking/GameTicker.GamePreset.cs b/Content.Server/GameTicking/GameTicker.GamePreset.cs index a1946d34a0a..fffacb59dee 100644 --- a/Content.Server/GameTicking/GameTicker.GamePreset.cs +++ b/Content.Server/GameTicking/GameTicker.GamePreset.cs @@ -274,35 +274,13 @@ public bool OnGhostAttempt(EntityUid mindId, bool canReturnGlobal, bool viaComma } } - var xformQuery = GetEntityQuery(); - var coords = _transform.GetMoverCoordinates(position, xformQuery); - - var ghost = Spawn(ObserverPrototypeName, coords); - - // Try setting the ghost entity name to either the character name or the player name. - // If all else fails, it'll default to the default entity prototype name, "observer". - // However, that should rarely happen. - if (!string.IsNullOrWhiteSpace(mind.CharacterName)) - _metaData.SetEntityName(ghost, mind.CharacterName); - else if (!string.IsNullOrWhiteSpace(mind.Session?.Name)) - _metaData.SetEntityName(ghost, mind.Session.Name); - - var ghostComponent = Comp(ghost); - - if (mind.TimeOfDeath.HasValue) - { - _ghost.SetTimeOfDeath(ghost, mind.TimeOfDeath!.Value, ghostComponent); - } + var ghost = _ghost.SpawnGhost((mindId, mind), position, canReturn); + if (ghost == null) + return false; if (playerEntity != null) _adminLogger.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} ghosted{(!canReturn ? " (non-returnable)" : "")}"); - _ghost.SetCanReturnToBody(ghostComponent, canReturn); - - if (canReturn) - _mind.Visit(mindId, ghost, mind); - else - _mind.TransferTo(mindId, ghost, mind: mind); return true; } diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index dfe8c5ce905..57ab1acce68 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -8,6 +8,7 @@ using Content.Server.Station.Components; using Content.Shared.CCVar; using Content.Shared.Database; +using Content.Shared.Mind; using Content.Shared.Players; using Content.Shared.Preferences; using Content.Shared.Roles; @@ -96,8 +97,7 @@ private void SpawnPlayers(List readyPlayers, if (job == null) { var playerSession = _playerManager.GetSessionById(netUser); - _chatManager.DispatchServerMessage(playerSession, - Loc.GetString("job-not-available-wait-in-lobby")); + _chatManager.DispatchServerMessage(playerSession, Loc.GetString("job-not-available-wait-in-lobby")); } else { @@ -323,10 +323,7 @@ public void Respawn(ICommonSession player) /// The station they're spawning on /// An optional job for them to spawn as /// Whether or not the player should be greeted upon joining - public void MakeJoinGame(ICommonSession player, - EntityUid station, - string? jobId = null, - bool silent = false) + public void MakeJoinGame(ICommonSession player, EntityUid station, string? jobId = null, bool silent = false) { if (!_playerGameStatuses.ContainsKey(player.UserId)) return; @@ -359,42 +356,29 @@ public void SpawnObserver(ICommonSession player) if (DummyTicker) return; - var mind = player.GetMind(); + Entity? mind = player.GetMind(); if (mind == null) { - mind = _mind.CreateMind(player.UserId); + var name = GetPlayerProfile(player).Name; + var (mindId, mindComp) = _mind.CreateMind(player.UserId, name); + mind = (mindId, mindComp); _mind.SetUserId(mind.Value, player.UserId); _roles.MindAddRole(mind.Value, new ObserverRoleComponent()); } - var name = GetPlayerProfile(player).Name; - var ghost = SpawnObserverMob(); - _metaData.SetEntityName(ghost, name); - _ghost.SetCanReturnToBody(ghost, false); - _mind.TransferTo(mind.Value, ghost); + var ghost = _ghost.SpawnGhost(mind.Value); _adminLogger.Add(LogType.LateJoin, LogImpact.Low, $"{player.Name} late joined the round as an Observer with {ToPrettyString(ghost):entity}."); } - #region Mob Spawning Helpers - - private EntityUid SpawnObserverMob() - { - var coordinates = GetObserverSpawnPoint(); - return EntityManager.SpawnEntity(ObserverPrototypeName, coordinates); - } - - #endregion - #region Spawn Points public EntityCoordinates GetObserverSpawnPoint() { _possiblePositions.Clear(); - foreach (var (point, transform) in EntityManager - .EntityQuery(true)) + foreach (var (point, transform) in EntityManager.EntityQuery(true)) { if (point.SpawnType != SpawnPointType.Observer) continue; @@ -410,7 +394,7 @@ public EntityCoordinates GetObserverSpawnPoint() var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var grid)) { - if (!metaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused) + if (!metaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused || TerminatingOrDeleted(uid)) { continue; } @@ -447,7 +431,9 @@ public EntityCoordinates GetObserverSpawnPoint() { var mapUid = _mapManager.GetMapEntityId(map); - if (!metaQuery.TryGetComponent(mapUid, out var meta) || meta.EntityPaused) + if (!metaQuery.TryGetComponent(mapUid, out var meta) + || meta.EntityPaused + || TerminatingOrDeleted(mapUid)) { continue; } diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 0be93d2054c..ac519b4c2e5 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -19,6 +19,7 @@ using Content.Shared.Storage.Components; using Robust.Server.GameObjects; using Robust.Server.Player; +using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; using Robust.Shared.Player; @@ -42,6 +43,8 @@ public sealed class GhostSystem : SharedGhostSystem [Dependency] private readonly GameTicker _ticker = default!; [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly IMapManager _mapManager = default!; private EntityQuery _ghostQuery; private EntityQuery _physicsQuery; @@ -389,5 +392,59 @@ public bool DoGhostBooEvent(EntityUid target) return ghostBoo.Handled; } + + public EntityUid? SpawnGhost(Entity mind, EntityUid targetEntity, + bool canReturn = false) + { + _transformSystem.TryGetMapOrGridCoordinates(targetEntity, out var spawnPosition); + return SpawnGhost(mind, spawnPosition, canReturn); + } + + public EntityUid? SpawnGhost(Entity mind, EntityCoordinates? spawnPosition = null, + bool canReturn = false) + { + if (!Resolve(mind, ref mind.Comp)) + return null; + + // Test if the map is being deleted + var mapUid = spawnPosition?.GetMapUid(EntityManager); + if (mapUid == null || TerminatingOrDeleted(mapUid.Value)) + spawnPosition = null; + + spawnPosition ??= _ticker.GetObserverSpawnPoint(); + + if (!spawnPosition.Value.IsValid(EntityManager)) + { + Log.Warning($"No spawn valid ghost spawn position found for {mind.Comp.CharacterName}" + + " \"{ToPrettyString(mind)}\""); + _minds.TransferTo(mind.Owner, null, createGhost: false, mind: mind.Comp); + return null; + } + + var ghost = SpawnAtPosition(GameTicker.ObserverPrototypeName, spawnPosition.Value); + var ghostComponent = Comp(ghost); + + // Try setting the ghost entity name to either the character name or the player name. + // If all else fails, it'll default to the default entity prototype name, "observer". + // However, that should rarely happen. + if (!string.IsNullOrWhiteSpace(mind.Comp.CharacterName)) + _metaData.SetEntityName(ghost, mind.Comp.CharacterName); + else if (!string.IsNullOrWhiteSpace(mind.Comp.Session?.Name)) + _metaData.SetEntityName(ghost, mind.Comp.Session.Name); + + if (mind.Comp.TimeOfDeath.HasValue) + { + SetTimeOfDeath(ghost, mind.Comp.TimeOfDeath!.Value, ghostComponent); + } + + SetCanReturnToBody(ghostComponent, canReturn); + + if (canReturn) + _minds.Visit(mind.Owner, ghost, mind.Comp); + else + _minds.TransferTo(mind.Owner, ghost, mind: mind.Comp); + Log.Debug($"Spawned ghost \"{ToPrettyString(ghost)}\" for {mind.Comp.CharacterName}."); + return ghost; + } } } diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs index dc12836d904..4271d76b445 100644 --- a/Content.Server/Mind/MindSystem.cs +++ b/Content.Server/Mind/MindSystem.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Server.Administration.Logs; using Content.Server.GameTicking; +using Content.Server.Ghost; using Content.Server.Mind.Commands; using Content.Shared.Database; using Content.Shared.Ghost; @@ -9,10 +10,8 @@ using Content.Shared.Players; using Robust.Server.GameStates; using Robust.Server.Player; -using Robust.Shared.Map.Components; using Robust.Shared.Network; using Robust.Shared.Player; -using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Mind; @@ -22,8 +21,7 @@ public sealed class MindSystem : SharedMindSystem [Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IPlayerManager _players = default!; - [Dependency] private readonly MetaDataSystem _metaData = default!; - [Dependency] private readonly SharedGhostSystem _ghosts = default!; + [Dependency] private readonly GhostSystem _ghosts = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly PvsOverrideSystem _pvsOverride = default!; @@ -63,8 +61,8 @@ private void OnMindContainerTerminating(EntityUid uid, MindContainerComponent co && !Terminating(visiting)) { TransferTo(mindId, visiting, mind: mind); - if (TryComp(visiting, out GhostComponent? ghost)) - _ghosts.SetCanReturnToBody(ghost, false); + if (TryComp(visiting, out GhostComponent? ghostComp)) + _ghosts.SetCanReturnToBody(ghostComp, false); return; } @@ -74,40 +72,13 @@ private void OnMindContainerTerminating(EntityUid uid, MindContainerComponent co if (!component.GhostOnShutdown || mind.Session == null || _gameTicker.RunLevel == GameRunLevel.PreRoundLobby) return; - var xform = Transform(uid); - var gridId = xform.GridUid; - var spawnPosition = Transform(uid).Coordinates; - - // Use a regular timer here because the entity has probably been deleted. - Timer.Spawn(0, () => - { - // Make extra sure the round didn't end between spawning the timer and it being executed. - if (_gameTicker.RunLevel == GameRunLevel.PreRoundLobby) - return; - - // Async this so that we don't throw if the grid we're on is being deleted. - if (!HasComp(gridId)) - spawnPosition = _gameTicker.GetObserverSpawnPoint(); - - // TODO refactor observer spawning. - // please. - if (!spawnPosition.IsValid(EntityManager)) - { - // This should be an error, if it didn't cause tests to start erroring when they delete a player. - Log.Warning($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, and no applicable spawn location is available."); - TransferTo(mindId, null, createGhost: false, mind: mind); - return; - } - - var ghost = Spawn(GameTicker.ObserverPrototypeName, spawnPosition); - var ghostComponent = Comp(ghost); - _ghosts.SetCanReturnToBody(ghostComponent, false); - + var ghost = _ghosts.SpawnGhost((mindId, mind), uid); + if (ghost != null) // Log these to make sure they're not causing the GameTicker round restart bugs... Log.Debug($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, spawned \"{ToPrettyString(ghost)}\"."); - _metaData.SetEntityName(ghost, mind.CharacterName ?? string.Empty); - TransferTo(mindId, ghost, mind: mind); - }); + else + // This should be an error, if it didn't cause tests to start erroring when they delete a player. + Log.Warning($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, and no applicable spawn location is available."); } public override bool TryGetMind(NetUserId user, [NotNullWhen(true)] out EntityUid? mindId, [NotNullWhen(true)] out MindComponent? mind) From c5b3e2861a178c2ee75791c481b14bd200c019d0 Mon Sep 17 00:00:00 2001 From: icekot8 <93311212+icekot8@users.noreply.github.com> Date: Sat, 11 May 2024 18:04:17 +0300 Subject: [PATCH 119/215] revenant can no longer harvest souls while in solid objects (#27612) meow --- .../Revenant/EntitySystems/RevenantSystem.Abilities.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index 68a26245000..4106630d526 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Numerics; using Content.Server.Revenant.Components; +using Content.Shared.Physics; using Content.Shared.DoAfter; using Content.Shared.Emag.Systems; using Content.Shared.FixedPoint; @@ -135,6 +136,12 @@ private void BeginHarvestDoAfter(EntityUid uid, EntityUid target, RevenantCompon return; } + if(_physics.GetEntitiesIntersectingBody(uid, (int) CollisionGroup.Impassable).Count > 0) + { + _popup.PopupEntity(Loc.GetString("revenant-in-solid"), uid, uid); + return; + } + var doAfter = new DoAfterArgs(EntityManager, uid, revenant.HarvestDebuffs.X, new HarvestEvent(), uid, target: target) { DistanceThreshold = 2, From fe58ec5f93cfe26de3abda53d86ab7bd5f1739b6 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 May 2024 15:04:48 +0000 Subject: [PATCH 120/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 24d0e6436f8..cc784ecdcf4 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Dutch-VanDerLinde - changes: - - message: Monkeys and kobolds can now wear blood-red hardsuits. - type: Add - id: 6084 - time: '2024-03-04T03:56:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/22273 - author: Plykiya changes: - message: Syringes now prevent injecting on empty and drawing on full. @@ -3865,3 +3858,10 @@ id: 6583 time: '2024-05-11T15:01:29.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27651 +- author: ShadowCommander + changes: + - message: Fixed screen freeze in some cases when getting gibbed or butchered. + type: Fix + id: 6584 + time: '2024-05-11T15:03:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27617 From af6a69448150c4ecba63f4dd18d39a3d6fff2a41 Mon Sep 17 00:00:00 2001 From: DrSmugleaf <10968691+drsmugleaf@users.noreply.github.com> Date: Sat, 11 May 2024 17:42:11 +0200 Subject: [PATCH 121/215] Make projectiles not hit crit mobs unless clicked on (#27905) --- .../Weapons/Ranged/Systems/GunSystem.cs | 8 ++++++++ .../Weapons/Ranged/Systems/GunSystem.cs | 7 +++++++ .../Systems/MobStateSystem.Subscribers.cs | 20 +++++++++++++++++++ .../Weapons/Ranged/Components/GunComponent.cs | 8 ++++++-- .../Components/TargetedProjectileComponent.cs | 12 +++++++++++ .../Ranged/Events/RequestShootEvent.cs | 1 + .../Weapons/Ranged/Systems/SharedGunSystem.cs | 2 ++ 7 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index 5aba04bdf8a..4a7711032e4 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Client.Animations; +using Content.Client.Gameplay; using Content.Client.Items; using Content.Client.Weapons.Ranged.Components; using Content.Shared.Camera; @@ -13,6 +14,7 @@ using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Client.Player; +using Robust.Client.State; using Robust.Shared.Animations; using Robust.Shared.Input; using Robust.Shared.Map; @@ -30,6 +32,7 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly IEyeManager _eyeManager = default!; [Dependency] private readonly IInputManager _inputManager = default!; [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IStateManager _state = default!; [Dependency] private readonly AnimationPlayerSystem _animPlayer = default!; [Dependency] private readonly InputSystem _inputSystem = default!; [Dependency] private readonly SharedCameraRecoilSystem _recoil = default!; @@ -174,10 +177,15 @@ public override void Update(float frameTime) // 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); + NetEntity? target = null; + if (_state.CurrentState is GameplayStateBase screen) + target = GetNetEntity(screen.GetClickedEntity(mousePos)); + Log.Debug($"Sending shoot request tick {Timing.CurTick} / {Timing.CurTime}"); EntityManager.RaisePredictiveEvent(new RequestShootEvent { + Target = target, Coordinates = GetNetCoordinates(coordinates), Gun = GetNetEntity(gunUid), }); diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index f495f29e4ae..986cac98ddf 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -278,6 +278,13 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, GunComponent gun, EntityUid gunUid, EntityUid? user) { + if (gun.Target is { } target && !TerminatingOrDeleted(target)) + { + var targeted = EnsureComp(uid); + targeted.Target = target; + Dirty(uid, targeted); + } + // Do a throw if (!HasComp(uid)) { diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs index 0c2fcc05794..ee747554e14 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -10,12 +10,15 @@ using Content.Shared.Mobs.Components; using Content.Shared.Movement.Events; using Content.Shared.Pointing; +using Content.Shared.Projectiles; using Content.Shared.Pulling.Events; using Content.Shared.Speech; using Content.Shared.Standing; using Content.Shared.Strip.Components; using Content.Shared.Throwing; +using Content.Shared.Weapons.Ranged.Components; using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Events; namespace Content.Shared.Mobs.Systems; @@ -43,6 +46,7 @@ private void SubscribeEvents() SubscribeLocalEvent(OnSleepAttempt); SubscribeLocalEvent(OnCombatModeShouldHandInteract); SubscribeLocalEvent(OnAttemptPacifiedAttack); + SubscribeLocalEvent(OnPreventCollide); } private void OnStateExitSubscribers(EntityUid target, MobStateComponent component, MobState state) @@ -175,5 +179,21 @@ private void OnAttemptPacifiedAttack(Entity ent, ref AttemptP args.Cancelled = true; } + private void OnPreventCollide(Entity ent, ref PreventCollideEvent args) + { + if (args.Cancelled) + return; + + if (IsAlive(ent, ent)) + return; + + var other = args.OtherEntity; + if (HasComp(other) && + CompOrNull(other)?.Target != ent.Owner) + { + args.Cancelled = true; + } + } + #endregion } diff --git a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs index 0183a30a73b..18fe4d4baa2 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs @@ -1,6 +1,4 @@ -using Content.Shared.Damage; using Content.Shared.Nyanotrasen.Abilities.Oni; -using Content.Shared.Tag; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Audio; @@ -140,6 +138,12 @@ public sealed partial class GunComponent : Component [ViewVariables] public EntityCoordinates? ShootCoordinates = null; + /// + /// Who the gun is being requested to shoot at directly. + /// + [ViewVariables] + public EntityUid? Target = null; + /// /// The base value for how many shots to fire per burst. /// diff --git a/Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs b/Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs new file mode 100644 index 00000000000..b804176497d --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Components/TargetedProjectileComponent.cs @@ -0,0 +1,12 @@ +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Ranged.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedGunSystem))] +public sealed partial class TargetedProjectileComponent : Component +{ + [DataField, AutoNetworkedField] + public EntityUid Target; +} diff --git a/Content.Shared/Weapons/Ranged/Events/RequestShootEvent.cs b/Content.Shared/Weapons/Ranged/Events/RequestShootEvent.cs index 21e90b2108b..f5c4dd72b4f 100644 --- a/Content.Shared/Weapons/Ranged/Events/RequestShootEvent.cs +++ b/Content.Shared/Weapons/Ranged/Events/RequestShootEvent.cs @@ -11,4 +11,5 @@ public sealed class RequestShootEvent : EntityEventArgs { public NetEntity Gun; public NetCoordinates Coordinates; + public NetEntity? Target; } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index c592a241419..89f062f98a4 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -138,6 +138,7 @@ private void OnShootRequest(RequestShootEvent msg, EntitySessionEventArgs args) return; gun.ShootCoordinates = GetCoordinates(msg.Coordinates); + gun.Target = GetEntity(msg.Target); AttemptShoot(user.Value, ent, gun); } @@ -198,6 +199,7 @@ private void StopShooting(EntityUid uid, GunComponent gun) gun.ShotCounter = 0; gun.ShootCoordinates = null; + gun.Target = null; Dirty(uid, gun); } From 0cd197cb7256e4a018f509fee042614bf3b20e7a Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 May 2024 15:43:17 +0000 Subject: [PATCH 122/215] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index cc784ecdcf4..ae8a6fe600b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: Plykiya - changes: - - message: Syringes now prevent injecting on empty and drawing on full. - type: Tweak - - message: You can now tell if you are being drawn from or injected by a syringe. - type: Tweak - id: 6085 - time: '2024-03-04T05:07:11.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25480 - author: metalgearsloth changes: - message: Fix expedition FTL. @@ -3865,3 +3856,11 @@ id: 6584 time: '2024-05-11T15:03:41.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/27617 +- author: DrSmugleaf + changes: + - message: Bullets no longer hit mobs that are in crit, unless they are directly + clicked on while shooting. + type: Tweak + id: 6585 + time: '2024-05-11T15:42:11.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/27905 From 27be1ad8c9bb841f1d0c9a9b40c6db677230bcaa Mon Sep 17 00:00:00 2001 From: keronshb <54602815+keronshb@users.noreply.github.com> Date: Sun, 12 May 2024 01:06:49 +0200 Subject: [PATCH 123/215] Magic Refactor + Wizard Grimoire (#22568) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Brings over changes from the original magic refactor PR * Adds Master Spellbook, spellbook categories, WizCoin currency, and locale * Wiz€oin™ * Adds currency whitelist to Spellbook preset, grants contained actions on action added. * Adds grant contained action and remove provided action. * adds a way for actions to be upgraded to the store * Adds Fireball 3 and fixes action upgrade logic so that it checks if the action can level or if the action can upgrade separately * Fixes upgrade logic in ActionUpgradeSystem to allow for level ups without an actual upgrade. Fixed action upgrade logic in store system as well * Removes current action entity from the bought entities list and adds new or old action entity * Removes Current Entity * Removes old comments, fixes TransferAllActionsWithNewAttached * Removes TODO * Removes Product Action Upgrade Event * reverts changes to immovablerodrule * Removes stale event reference * fixes mind action grant logic * reverts shared gun system change to projectile anomaly system * forgor to remove the using * Reverts unintended changes to action container * Adds refund button to the store * Refreshes store back to origin. * Refund with correct currency * Init refund * Check for terminating and update interface * Disables refund button * Removes preset allow refund * dont refund if map changed * adds refunds to stores * Adds method to check for starting map * comments, datafields, some requested changes * turns event into ref event * Adds datafields * Switches to entity terminating event * Changes store entity to be nullable and checks if store is terminating to remove reference. * Tryadd instead of containskey * Adds a refund disable method, disables refund on bought ent container changes if not an action * Removes duplicate refundcomp * Removes unintended merges * Removed another unintended change from merge * removes extra using statement * readds using statement * might as well just remove both usings since it won't leave the PR * Fixes Action upgrades from stores * Changes to non obsolete method uses * Shares spawn code between instant and world * Adds action entity to action event, adds beforecastspellevent, adds spell requirements to magic component * puts prereq check in spell methods, sets up template code for before cast event * checks for required wizard clothes * Networks Magic Comp and Wizard Clothes Comp. Renames MagicSpawnData to MagicInstantSpawnData. * Removes posdata from projectiles * Speech > RequiresSpeech * Fixes ActionOnInteract * checks for muted * popup for missing reqs * Validate click loc for blink spell * Checks if doors are in range and not obstructed before opening * Check ents by map coords * Adds speak event * Comments spellbooks * Removes comments * Unobsoletes smite spell * Invert if * Requirements loc * Fixes spell reqs * Inverts an if * Comment updates * Starts doafter work * Removes doafter references * Balances fireball upgrades to be more reasonable * Enables refund on master spellbooks * Spells to do * update spellbook doafter * knock toggles bolts * Touch Spell comments * Comments for pending spells * more comments * adds spider polymorph to spellbook * TODOs for spells * reorganizes spellbook categories and adds wands * fixes spacing and adds limited conditions * commented owner only for future store PR * reenables owner only for the grimoire * fixes grimoire sprite * Adds wizard rod polymorph * summon ghosts event * Moves rod form to offensive category * Adds charge spell and loc for rod polymorph * Oops forgor the actual chages * Item Recall comment * Fixes UI * removes extra field for wizard rod * Cleanup * New Condition (INCOMPLETE) * Fix linter * Fix linter (for real) * fixed some descriptions * adds regions to magic * Adds a non-refund wizard grimoire, fixes blink to deselect after teleporting, reduces force wall despawn time to 12 seconds * removes limited upgrade condition --------- Co-authored-by: AJCM --- Content.Client/Magic/MagicSystem.cs | 5 + .../Actions/ActionOnInteractSystem.cs | 25 +- Content.Server/Ghost/GhostSystem.cs | 10 + Content.Server/Magic/MagicSystem.cs | 393 +------------ .../Store/Systems/StoreSystem.Refund.cs | 2 +- .../Store/Systems/StoreSystem.Ui.cs | 6 +- Content.Shared/Actions/ActionEvents.cs | 2 +- Content.Shared/Actions/SharedActionsSystem.cs | 7 +- Content.Shared/Ghost/GhostComponent.cs | 2 + .../Magic/Components/MagicComponent.cs | 39 ++ .../Magic/Components/SpellbookComponent.cs | 17 +- .../Components/WizardClothesComponent.cs | 10 + .../Magic/Events/BeforeCastSpellEvent.cs | 12 + .../Magic/Events/ChargeSpellEvent.cs | 18 + .../Magic/Events/InstantSpawnSpellEvent.cs | 12 +- .../Magic/Events/KnockSpellEvent.cs | 19 +- .../Magic/Events/ProjectileSpellEvent.cs | 12 +- .../Magic/Events/SmiteSpellEvent.cs | 7 +- .../Magic/Events/SpeakSpellEvent.cs | 8 + .../Magic/Events/TeleportSpellEvent.cs | 12 +- .../Magic/Events/WorldSpawnSpellEvent.cs | 16 +- Content.Shared/Magic/MagicInstantSpawnData.cs | 25 + Content.Shared/Magic/MagicSpawnData.cs | 20 - Content.Shared/Magic/SharedMagicSystem.cs | 519 ++++++++++++++++++ Content.Shared/Magic/SpellbookSystem.cs | 96 ++++ Content.Shared/Store/ListingPrototype.cs | 10 +- Resources/Locale/en-US/magic/magic.ftl | 1 + Resources/Locale/en-US/store/categories.ftl | 8 + Resources/Locale/en-US/store/currency.ftl | 1 + .../Locale/en-US/store/spellbook-catalog.ftl | 35 ++ Resources/Prototypes/Actions/polymorph.yml | 30 + .../Prototypes/Catalog/spellbook_catalog.yml | 140 +++++ .../Entities/Clothing/Head/hats.yml | 12 +- .../Entities/Clothing/OuterClothing/misc.yml | 13 +- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 12 + .../Entities/Objects/Magic/books.yml | 61 +- .../Entities/Structures/Walls/walls.yml | 2 +- Resources/Prototypes/Magic/event_spells.yml | 13 + Resources/Prototypes/Magic/knock_spell.yml | 2 + .../Prototypes/Magic/projectile_spells.yml | 34 +- .../Prototypes/Magic/teleport_spells.yml | 4 +- Resources/Prototypes/Magic/utility_spells.yml | 15 + Resources/Prototypes/Polymorphs/polymorph.yml | 22 + Resources/Prototypes/Store/categories.yml | 26 + Resources/Prototypes/Store/currency.yml | 9 +- Resources/Prototypes/Store/presets.yml | 12 + 46 files changed, 1254 insertions(+), 502 deletions(-) create mode 100644 Content.Client/Magic/MagicSystem.cs create mode 100644 Content.Shared/Magic/Components/MagicComponent.cs rename {Content.Server => Content.Shared}/Magic/Components/SpellbookComponent.cs (60%) create mode 100644 Content.Shared/Magic/Components/WizardClothesComponent.cs create mode 100644 Content.Shared/Magic/Events/BeforeCastSpellEvent.cs create mode 100644 Content.Shared/Magic/Events/ChargeSpellEvent.cs create mode 100644 Content.Shared/Magic/Events/SpeakSpellEvent.cs create mode 100644 Content.Shared/Magic/MagicInstantSpawnData.cs delete mode 100644 Content.Shared/Magic/MagicSpawnData.cs create mode 100644 Content.Shared/Magic/SharedMagicSystem.cs create mode 100644 Content.Shared/Magic/SpellbookSystem.cs create mode 100644 Resources/Locale/en-US/magic/magic.ftl create mode 100644 Resources/Locale/en-US/store/spellbook-catalog.ftl create mode 100644 Resources/Prototypes/Catalog/spellbook_catalog.yml create mode 100644 Resources/Prototypes/Magic/event_spells.yml create mode 100644 Resources/Prototypes/Magic/utility_spells.yml diff --git a/Content.Client/Magic/MagicSystem.cs b/Content.Client/Magic/MagicSystem.cs new file mode 100644 index 00000000000..03aa9eb56d4 --- /dev/null +++ b/Content.Client/Magic/MagicSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Magic; + +namespace Content.Client.Magic; + +public sealed class MagicSystem : SharedMagicSystem; diff --git a/Content.Server/Actions/ActionOnInteractSystem.cs b/Content.Server/Actions/ActionOnInteractSystem.cs index 657ab46d60c..b6eec0ce0f6 100644 --- a/Content.Server/Actions/ActionOnInteractSystem.cs +++ b/Content.Server/Actions/ActionOnInteractSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Shared.Actions; using Content.Shared.Interaction; using Robust.Shared.Random; @@ -38,10 +39,18 @@ private void OnMapInit(EntityUid uid, ActionOnInteractComponent component, MapIn private void OnActivate(EntityUid uid, ActionOnInteractComponent component, ActivateInWorldEvent args) { - if (args.Handled || component.ActionEntities == null) + if (args.Handled) return; - var options = GetValidActions(component.ActionEntities); + if (component.ActionEntities is not {} actionEnts) + { + if (!TryComp(uid, out var actionsContainerComponent)) + return; + + actionEnts = actionsContainerComponent.Container.ContainedEntities.ToList(); + } + + var options = GetValidActions(actionEnts); if (options.Count == 0) return; @@ -58,13 +67,21 @@ private void OnActivate(EntityUid uid, ActionOnInteractComponent component, Acti private void OnAfterInteract(EntityUid uid, ActionOnInteractComponent component, AfterInteractEvent args) { - if (args.Handled || component.ActionEntities == null) + if (args.Handled) return; + if (component.ActionEntities is not {} actionEnts) + { + if (!TryComp(uid, out var actionsContainerComponent)) + return; + + actionEnts = actionsContainerComponent.Container.ContainedEntities.ToList(); + } + // First, try entity target actions if (args.Target != null) { - var entOptions = GetValidActions(component.ActionEntities, args.CanReach); + var entOptions = GetValidActions(actionEnts, args.CanReach); for (var i = entOptions.Count - 1; i >= 0; i--) { var action = entOptions[i]; diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index ac519b4c2e5..b1fb67cce7b 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -78,6 +78,7 @@ public override void Initialize() SubscribeLocalEvent(OnEntityStorageInsertAttempt); SubscribeLocalEvent(_ => MakeVisible(true)); + SubscribeLocalEvent(OnToggleGhostVisibilityToAll); } private void OnGhostHearingAction(EntityUid uid, GhostComponent component, ToggleGhostHearingActionEvent args) @@ -363,6 +364,15 @@ private void OnEntityStorageInsertAttempt(EntityUid uid, GhostComponent comp, re args.Cancelled = true; } + private void OnToggleGhostVisibilityToAll(ToggleGhostVisibilityToAllEvent ev) + { + if (ev.Handled) + return; + + ev.Handled = true; + MakeVisible(true); + } + /// /// When the round ends, make all players able to see ghosts. /// diff --git a/Content.Server/Magic/MagicSystem.cs b/Content.Server/Magic/MagicSystem.cs index f7250c01ba5..2cf5136b427 100644 --- a/Content.Server/Magic/MagicSystem.cs +++ b/Content.Server/Magic/MagicSystem.cs @@ -1,407 +1,22 @@ -using System.Numerics; -using Content.Server.Body.Components; -using Content.Server.Body.Systems; using Content.Server.Chat.Systems; -using Content.Server.Doors.Systems; -using Content.Server.Magic.Components; -using Content.Server.Weapons.Ranged.Systems; -using Content.Shared.Actions; -using Content.Shared.Body.Components; -using Content.Shared.Coordinates.Helpers; -using Content.Shared.DoAfter; -using Content.Shared.Doors.Components; -using Content.Shared.Doors.Systems; -using Content.Shared.Interaction.Events; using Content.Shared.Magic; using Content.Shared.Magic.Events; -using Content.Shared.Maps; -using Content.Shared.Physics; -using Content.Shared.Storage; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; -using Robust.Shared.Serialization.Manager; -using Robust.Shared.Spawners; namespace Content.Server.Magic; -/// -/// Handles learning and using spells (actions) -/// -public sealed class MagicSystem : EntitySystem +public sealed class MagicSystem : SharedMagicSystem { - [Dependency] private readonly ISerializationManager _seriMan = default!; - [Dependency] private readonly IComponentFactory _compFact = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly BodySystem _bodySystem = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly SharedDoorSystem _doorSystem = default!; - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly GunSystem _gunSystem = default!; - [Dependency] private readonly PhysicsSystem _physics = default!; - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly ActionContainerSystem _actionContainer = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnUse); - SubscribeLocalEvent(OnDoAfter); - - SubscribeLocalEvent(OnInstantSpawn); - SubscribeLocalEvent(OnTeleportSpell); - SubscribeLocalEvent(OnKnockSpell); - SubscribeLocalEvent(OnSmiteSpell); - SubscribeLocalEvent(OnWorldSpawn); - SubscribeLocalEvent(OnProjectileSpell); - SubscribeLocalEvent(OnChangeComponentsSpell); - } - - private void OnDoAfter(EntityUid uid, SpellbookComponent component, DoAfterEvent args) - { - if (args.Handled || args.Cancelled) - return; - - args.Handled = true; - if (!component.LearnPermanently) - { - _actionsSystem.GrantActions(args.Args.User, component.Spells, uid); - return; - } - - foreach (var (id, charges) in component.SpellActions) - { - // TOOD store spells entity ids on some sort of innate magic user component or something like that. - EntityUid? actionId = null; - if (_actionsSystem.AddAction(args.Args.User, ref actionId, id)) - _actionsSystem.SetCharges(actionId, charges < 0 ? null : charges); - } - - component.SpellActions.Clear(); - } - - private void OnInit(EntityUid uid, SpellbookComponent component, MapInitEvent args) - { - if (component.LearnPermanently) - return; - - foreach (var (id, charges) in component.SpellActions) - { - var spell = _actionContainer.AddAction(uid, id); - if (spell == null) - continue; - - _actionsSystem.SetCharges(spell, charges < 0 ? null : charges); - component.Spells.Add(spell.Value); - } - } - - private void OnUse(EntityUid uid, SpellbookComponent component, UseInHandEvent args) - { - if (args.Handled) - return; - - AttemptLearn(uid, component, args); - - args.Handled = true; - } - - private void AttemptLearn(EntityUid uid, SpellbookComponent component, UseInHandEvent args) - { - var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.LearnTime, new SpellbookDoAfterEvent(), uid, target: uid) - { - BreakOnDamage = true, - BreakOnMove = true, - NeedHand = true //What, are you going to read with your eyes only?? - }; - - _doAfter.TryStartDoAfter(doAfterEventArgs); - } - - #region Spells - - /// - /// Handles the instant action (i.e. on the caster) attempting to spawn an entity. - /// - private void OnInstantSpawn(InstantSpawnSpellEvent args) - { - if (args.Handled) - return; - - var transform = Transform(args.Performer); - - foreach (var position in GetSpawnPositions(transform, args.Pos)) - { - var ent = Spawn(args.Prototype, position.SnapToGrid(EntityManager, _mapManager)); - - if (args.PreventCollideWithCaster) - { - var comp = EnsureComp(ent); - comp.Uid = args.Performer; - } - } - - Speak(args); - args.Handled = true; - } - - private void OnProjectileSpell(ProjectileSpellEvent ev) - { - if (ev.Handled) - return; - - ev.Handled = true; - Speak(ev); - - var xform = Transform(ev.Performer); - var userVelocity = _physics.GetMapLinearVelocity(ev.Performer); - - foreach (var pos in GetSpawnPositions(xform, ev.Pos)) - { - // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. - var mapPos = pos.ToMap(EntityManager, _transformSystem); - var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out _) - ? pos.WithEntityId(gridUid, EntityManager) - : new(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position); - - var ent = Spawn(ev.Prototype, spawnCoords); - var direction = ev.Target.ToMapPos(EntityManager, _transformSystem) - - spawnCoords.ToMapPos(EntityManager, _transformSystem); - _gunSystem.ShootProjectile(ent, direction, userVelocity, ev.Performer, ev.Performer); - } - } - - private void OnChangeComponentsSpell(ChangeComponentsSpellEvent ev) - { - if (ev.Handled) - return; - ev.Handled = true; - Speak(ev); - - foreach (var toRemove in ev.ToRemove) - { - if (_compFact.TryGetRegistration(toRemove, out var registration)) - RemComp(ev.Target, registration.Type); - } - - foreach (var (name, data) in ev.ToAdd) - { - if (HasComp(ev.Target, data.Component.GetType())) - continue; - - var component = (Component) _compFact.GetComponent(name); - component.Owner = ev.Target; - var temp = (object) component; - _seriMan.CopyTo(data.Component, ref temp); - EntityManager.AddComponent(ev.Target, (Component) temp!); - } - } - - private List GetSpawnPositions(TransformComponent casterXform, MagicSpawnData data) - { - switch (data) - { - case TargetCasterPos: - return new List(1) {casterXform.Coordinates}; - case TargetInFront: - { - // This is shit but you get the idea. - var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized()); - - if (!TryComp(casterXform.GridUid, out var mapGrid)) - return new List(); - - if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager)) - return new List(); - - var tileIndex = tileReference.Value.GridIndices; - var coords = mapGrid.GridTileToLocal(tileIndex); - EntityCoordinates coordsPlus; - EntityCoordinates coordsMinus; - - var dir = casterXform.LocalRotation.GetCardinalDir(); - switch (dir) - { - case Direction.North: - case Direction.South: - { - coordsPlus = mapGrid.GridTileToLocal(tileIndex + (1, 0)); - coordsMinus = mapGrid.GridTileToLocal(tileIndex + (-1, 0)); - return new List(3) - { - coords, - coordsPlus, - coordsMinus, - }; - } - case Direction.East: - case Direction.West: - { - coordsPlus = mapGrid.GridTileToLocal(tileIndex + (0, 1)); - coordsMinus = mapGrid.GridTileToLocal(tileIndex + (0, -1)); - return new List(3) - { - coords, - coordsPlus, - coordsMinus, - }; - } - } - - return new List(); - } - default: - throw new ArgumentOutOfRangeException(); - } - } - - /// - /// Teleports the user to the clicked location - /// - /// - private void OnTeleportSpell(TeleportSpellEvent args) - { - if (args.Handled) - return; - - var transform = Transform(args.Performer); - - if (transform.MapID != args.Target.GetMapId(EntityManager)) return; - - _transformSystem.SetCoordinates(args.Performer, args.Target); - transform.AttachToGridOrMap(); - _audio.PlayPvs(args.BlinkSound, args.Performer, AudioParams.Default.WithVolume(args.BlinkVolume)); - Speak(args); - args.Handled = true; + SubscribeLocalEvent(OnSpellSpoken); } - /// - /// Opens all doors within range - /// - /// - private void OnKnockSpell(KnockSpellEvent args) + private void OnSpellSpoken(ref SpeakSpellEvent args) { - if (args.Handled) - return; - - args.Handled = true; - Speak(args); - - //Get the position of the player - var transform = Transform(args.Performer); - var coords = transform.Coordinates; - - _audio.PlayPvs(args.KnockSound, args.Performer, AudioParams.Default.WithVolume(args.KnockVolume)); - - //Look for doors and don't open them if they're already open. - foreach (var entity in _lookup.GetEntitiesInRange(coords, args.Range)) - { - if (TryComp(entity, out var bolts)) - _doorSystem.SetBoltsDown((entity, bolts), false); - - if (TryComp(entity, out var doorComp) && doorComp.State is not DoorState.Open) - _doorSystem.StartOpening(entity); - } - } - - private void OnSmiteSpell(SmiteSpellEvent ev) - { - if (ev.Handled) - return; - - ev.Handled = true; - Speak(ev); - - var direction = Transform(ev.Target).MapPosition.Position - Transform(ev.Performer).MapPosition.Position; - var impulseVector = direction * 10000; - - _physics.ApplyLinearImpulse(ev.Target, impulseVector); - - if (!TryComp(ev.Target, out var body)) - return; - - var ents = _bodySystem.GibBody(ev.Target, true, body); - - if (!ev.DeleteNonBrainParts) - return; - - foreach (var part in ents) - { - // just leaves a brain and clothes - if (HasComp(part) && !HasComp(part)) - { - QueueDel(part); - } - } - } - - /// - /// Spawns entity prototypes from a list within range of click. - /// - /// - /// It will offset mobs after the first mob based on the OffsetVector2 property supplied. - /// - /// The Spawn Spell Event args. - private void OnWorldSpawn(WorldSpawnSpellEvent args) - { - if (args.Handled) - return; - - var targetMapCoords = args.Target; - - SpawnSpellHelper(args.Contents, targetMapCoords, args.Lifetime, args.Offset); - Speak(args); - args.Handled = true; - } - - /// - /// Loops through a supplied list of entity prototypes and spawns them - /// - /// - /// If an offset of 0, 0 is supplied then the entities will all spawn on the same tile. - /// Any other offset will spawn entities starting from the source Map Coordinates and will increment the supplied - /// offset - /// - /// The list of Entities to spawn in - /// Map Coordinates where the entities will spawn - /// Check to see if the entities should self delete - /// A Vector2 offset that the entities will spawn in - private void SpawnSpellHelper(List entityEntries, EntityCoordinates entityCoords, float? lifetime, Vector2 offsetVector2) - { - var getProtos = EntitySpawnCollection.GetSpawns(entityEntries, _random); - - var offsetCoords = entityCoords; - foreach (var proto in getProtos) - { - // TODO: Share this code with instant because they're both doing similar things for positioning. - var entity = Spawn(proto, offsetCoords); - offsetCoords = offsetCoords.Offset(offsetVector2); - - if (lifetime != null) - { - var comp = EnsureComp(entity); - comp.Lifetime = lifetime.Value; - } - } - } - - #endregion - - private void Speak(BaseActionEvent args) - { - if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech)) - return; - - _chat.TrySendInGameICMessage(args.Performer, Loc.GetString(speak.Speech), - InGameICChatType.Speak, false); + _chat.TrySendInGameICMessage(args.Performer, Loc.GetString(args.Speech), InGameICChatType.Speak, false); } } diff --git a/Content.Server/Store/Systems/StoreSystem.Refund.cs b/Content.Server/Store/Systems/StoreSystem.Refund.cs index d59ee75e3ea..5a8be4be2bb 100644 --- a/Content.Server/Store/Systems/StoreSystem.Refund.cs +++ b/Content.Server/Store/Systems/StoreSystem.Refund.cs @@ -1,4 +1,4 @@ -using Content.Server.Store.Components; +using Content.Server.Store.Components; using Robust.Shared.Containers; namespace Content.Server.Store.Systems; diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index fa363c54c12..0a1a8d19f31 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -215,11 +215,11 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi { HandleRefundComp(uid, component, actionId.Value); - if (listing.ProductUpgradeID != null) + if (listing.ProductUpgradeId != null) { foreach (var upgradeListing in component.Listings) { - if (upgradeListing.ID == listing.ProductUpgradeID) + if (upgradeListing.ID == listing.ProductUpgradeId) { upgradeListing.ProductActionEntity = actionId.Value; break; @@ -229,7 +229,7 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi } } - if (listing is { ProductUpgradeID: not null, ProductActionEntity: not null }) + if (listing is { ProductUpgradeId: not null, ProductActionEntity: not null }) { if (listing.ProductActionEntity != null) { diff --git a/Content.Shared/Actions/ActionEvents.cs b/Content.Shared/Actions/ActionEvents.cs index c6002d0d4ad..6cc50bc21b4 100644 --- a/Content.Shared/Actions/ActionEvents.cs +++ b/Content.Shared/Actions/ActionEvents.cs @@ -157,7 +157,7 @@ public abstract partial class BaseActionEvent : HandledEntityEventArgs public EntityUid Performer; /// - /// The action that was performed. + /// The action the event belongs to. /// public EntityUid Action; } diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 315d2725b2e..30687c93225 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -569,13 +569,12 @@ public void PerformAction(EntityUid performer, ActionsComponent? component, Enti handled = actionEvent.Handled; } - _audio.PlayPredicted(action.Sound, performer,predicted ? performer : null); - handled |= action.Sound != null; - if (!handled) return; // no interaction occurred. - // reduce charges, start cooldown, and mark as dirty (if required). + // play sound, reduce charges, start cooldown, and mark as dirty (if required). + + _audio.PlayPredicted(action.Sound, performer,predicted ? performer : null); var dirty = toggledBefore == action.Toggled; diff --git a/Content.Shared/Ghost/GhostComponent.cs b/Content.Shared/Ghost/GhostComponent.cs index f7717e8d232..96e9b717b90 100644 --- a/Content.Shared/Ghost/GhostComponent.cs +++ b/Content.Shared/Ghost/GhostComponent.cs @@ -101,4 +101,6 @@ public sealed partial class ToggleLightingActionEvent : InstantActionEvent { } public sealed partial class ToggleGhostHearingActionEvent : InstantActionEvent { } +public sealed partial class ToggleGhostVisibilityToAllEvent : InstantActionEvent { } + public sealed partial class BooActionEvent : InstantActionEvent { } diff --git a/Content.Shared/Magic/Components/MagicComponent.cs b/Content.Shared/Magic/Components/MagicComponent.cs new file mode 100644 index 00000000000..bcc11063b71 --- /dev/null +++ b/Content.Shared/Magic/Components/MagicComponent.cs @@ -0,0 +1,39 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Magic.Components; + +// TODO: Rename to MagicActionComponent or MagicRequirementsComponent +[RegisterComponent, NetworkedComponent, Access(typeof(SharedMagicSystem))] +public sealed partial class MagicComponent : Component +{ + // TODO: Split into different components? + // This could be the MagicRequirementsComp - which just is requirements for the spell + // Magic comp could be on the actual entities itself + // Could handle lifetime, ignore caster, etc? + // Magic caster comp would be on the caster, used for what I'm not sure + + // TODO: Do After here or in actions + + // TODO: Spell requirements + // A list of requirements to cast the spell + // Hands + // Any item in hand + // Spell takes up an inhand slot + // May be an action toggle or something + + // TODO: List requirements in action desc + /// + /// Does this spell require Wizard Robes & Hat? + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool RequiresClothes; + + /// + /// Does this spell require the user to speak? + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool RequiresSpeech; + + // TODO: FreeHand - should check if toggleable action + // Check which hand is free to toggle action in +} diff --git a/Content.Server/Magic/Components/SpellbookComponent.cs b/Content.Shared/Magic/Components/SpellbookComponent.cs similarity index 60% rename from Content.Server/Magic/Components/SpellbookComponent.cs rename to Content.Shared/Magic/Components/SpellbookComponent.cs index ebc3c880436..f1b307c245d 100644 --- a/Content.Server/Magic/Components/SpellbookComponent.cs +++ b/Content.Shared/Magic/Components/SpellbookComponent.cs @@ -1,12 +1,13 @@ using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -namespace Content.Server.Magic.Components; +namespace Content.Shared.Magic.Components; /// -/// Spellbooks for having an entity learn spells as long as they've read the book and it's in their hand. +/// Spellbooks can grant one or more spells to the user. If marked as it will teach +/// the performer the spells and wipe the book. +/// Default behavior requires the book to be held in hand /// -[RegisterComponent] +[RegisterComponent, Access(typeof(SpellbookSystem))] public sealed partial class SpellbookComponent : Component { /// @@ -18,18 +19,18 @@ public sealed partial class SpellbookComponent : Component /// /// The three fields below is just used for initialization. /// - [DataField("spells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + [DataField] [ViewVariables(VVAccess.ReadWrite)] - public Dictionary SpellActions = new(); + public Dictionary SpellActions = new(); - [DataField("learnTime")] + [DataField] [ViewVariables(VVAccess.ReadWrite)] public float LearnTime = .75f; /// /// If true, the spell action stays even after the book is removed /// - [DataField("learnPermanently")] + [DataField] [ViewVariables(VVAccess.ReadWrite)] public bool LearnPermanently; } diff --git a/Content.Shared/Magic/Components/WizardClothesComponent.cs b/Content.Shared/Magic/Components/WizardClothesComponent.cs new file mode 100644 index 00000000000..063cf56c338 --- /dev/null +++ b/Content.Shared/Magic/Components/WizardClothesComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Magic.Components; + +/// +/// The checks this if a spell requires wizard clothes +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedMagicSystem))] +public sealed partial class WizardClothesComponent : Component; diff --git a/Content.Shared/Magic/Events/BeforeCastSpellEvent.cs b/Content.Shared/Magic/Events/BeforeCastSpellEvent.cs new file mode 100644 index 00000000000..afb5c1f090d --- /dev/null +++ b/Content.Shared/Magic/Events/BeforeCastSpellEvent.cs @@ -0,0 +1,12 @@ +namespace Content.Shared.Magic.Events; + +[ByRefEvent] +public struct BeforeCastSpellEvent(EntityUid performer) +{ + /// + /// The Performer of the event, to check if they meet the requirements. + /// + public EntityUid Performer = performer; + + public bool Cancelled; +} diff --git a/Content.Shared/Magic/Events/ChargeSpellEvent.cs b/Content.Shared/Magic/Events/ChargeSpellEvent.cs new file mode 100644 index 00000000000..8898761ec2a --- /dev/null +++ b/Content.Shared/Magic/Events/ChargeSpellEvent.cs @@ -0,0 +1,18 @@ +using Content.Shared.Actions; + +namespace Content.Shared.Magic.Events; + +/// +/// Adds provided Charge to the held wand +/// +public sealed partial class ChargeSpellEvent : InstantActionEvent, ISpeakSpell +{ + [DataField(required: true)] + public int Charge; + + [DataField] + public string WandTag = "WizardWand"; + + [DataField] + public string? Speech { get; private set; } +} diff --git a/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs b/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs index ef8d6898623..1405b158271 100644 --- a/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs +++ b/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs @@ -1,6 +1,5 @@ using Content.Shared.Actions; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Magic.Events; @@ -9,17 +8,18 @@ public sealed partial class InstantSpawnSpellEvent : InstantActionEvent, ISpeakS /// /// What entity should be spawned. /// - [DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Prototype = default!; + [DataField(required: true)] + public EntProtoId Prototype; - [DataField("preventCollide")] + [DataField] public bool PreventCollideWithCaster = true; - [DataField("speech")] + [DataField] public string? Speech { get; private set; } /// /// Gets the targeted spawn positons; may lead to multiple entities being spawned. /// - [DataField("posData")] public MagicSpawnData Pos = new TargetCasterPos(); + [DataField] + public MagicInstantSpawnData PosData = new TargetCasterPos(); } diff --git a/Content.Shared/Magic/Events/KnockSpellEvent.cs b/Content.Shared/Magic/Events/KnockSpellEvent.cs index a3b0be55759..24a1700d21f 100644 --- a/Content.Shared/Magic/Events/KnockSpellEvent.cs +++ b/Content.Shared/Magic/Events/KnockSpellEvent.cs @@ -1,5 +1,4 @@ using Content.Shared.Actions; -using Robust.Shared.Audio; namespace Content.Shared.Magic.Events; @@ -7,20 +6,12 @@ public sealed partial class KnockSpellEvent : InstantActionEvent, ISpeakSpell { /// /// The range this spell opens doors in - /// 4f is the default + /// 10f is the default + /// Should be able to open all doors/lockers in visible sight /// - [DataField("range")] - public float Range = 4f; + [DataField] + public float Range = 10f; - [DataField("knockSound")] - public SoundSpecifier KnockSound = new SoundPathSpecifier("/Audio/Magic/knock.ogg"); - - /// - /// Volume control for the spell. - /// - [DataField("knockVolume")] - public float KnockVolume = 5f; - - [DataField("speech")] + [DataField] public string? Speech { get; private set; } } diff --git a/Content.Shared/Magic/Events/ProjectileSpellEvent.cs b/Content.Shared/Magic/Events/ProjectileSpellEvent.cs index 44966257699..336ea03346b 100644 --- a/Content.Shared/Magic/Events/ProjectileSpellEvent.cs +++ b/Content.Shared/Magic/Events/ProjectileSpellEvent.cs @@ -1,6 +1,5 @@ using Content.Shared.Actions; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Magic.Events; @@ -9,14 +8,9 @@ public sealed partial class ProjectileSpellEvent : WorldTargetActionEvent, ISpea /// /// What entity should be spawned. /// - [DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Prototype = default!; + [DataField(required: true)] + public EntProtoId Prototype; - /// - /// Gets the targeted spawn positions; may lead to multiple entities being spawned. - /// - [DataField("posData")] public MagicSpawnData Pos = new TargetCasterPos(); - - [DataField("speech")] + [DataField] public string? Speech { get; private set; } } diff --git a/Content.Shared/Magic/Events/SmiteSpellEvent.cs b/Content.Shared/Magic/Events/SmiteSpellEvent.cs index 08ec63c05e7..74ca116ad59 100644 --- a/Content.Shared/Magic/Events/SmiteSpellEvent.cs +++ b/Content.Shared/Magic/Events/SmiteSpellEvent.cs @@ -4,12 +4,13 @@ namespace Content.Shared.Magic.Events; public sealed partial class SmiteSpellEvent : EntityTargetActionEvent, ISpeakSpell { + // TODO: Make part of gib method /// - /// Should this smite delete all parts/mechanisms gibbed except for the brain? + /// Should this smite delete all parts/mechanisms gibbed except for the brain? /// - [DataField("deleteNonBrainParts")] + [DataField] public bool DeleteNonBrainParts = true; - [DataField("speech")] + [DataField] public string? Speech { get; private set; } } diff --git a/Content.Shared/Magic/Events/SpeakSpellEvent.cs b/Content.Shared/Magic/Events/SpeakSpellEvent.cs new file mode 100644 index 00000000000..1b3f7af63c3 --- /dev/null +++ b/Content.Shared/Magic/Events/SpeakSpellEvent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared.Magic.Events; + +[ByRefEvent] +public readonly struct SpeakSpellEvent(EntityUid performer, string speech) +{ + public readonly EntityUid Performer = performer; + public readonly string Speech = speech; +} diff --git a/Content.Shared/Magic/Events/TeleportSpellEvent.cs b/Content.Shared/Magic/Events/TeleportSpellEvent.cs index b24f6ec72f7..525c1e51052 100644 --- a/Content.Shared/Magic/Events/TeleportSpellEvent.cs +++ b/Content.Shared/Magic/Events/TeleportSpellEvent.cs @@ -1,19 +1,19 @@ using Content.Shared.Actions; -using Robust.Shared.Audio; namespace Content.Shared.Magic.Events; +// TODO: Can probably just be an entity or something public sealed partial class TeleportSpellEvent : WorldTargetActionEvent, ISpeakSpell { - [DataField("blinkSound")] - public SoundSpecifier BlinkSound = new SoundPathSpecifier("/Audio/Magic/blink.ogg"); - - [DataField("speech")] + [DataField] public string? Speech { get; private set; } + // TODO: Move to magic component + // TODO: Maybe not since sound specifier is a thing + // Keep here to remind what the volume was set as /// /// Volume control for the spell. /// - [DataField("blinkVolume")] + [DataField] public float BlinkVolume = 5f; } diff --git a/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs b/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs index 4355cab8421..2f50c67b3e7 100644 --- a/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs +++ b/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs @@ -4,29 +4,31 @@ namespace Content.Shared.Magic.Events; +// TODO: This class needs combining with InstantSpawnSpellEvent + public sealed partial class WorldSpawnSpellEvent : WorldTargetActionEvent, ISpeakSpell { - // TODO:This class needs combining with InstantSpawnSpellEvent - /// /// The list of prototypes this spell will spawn /// - [DataField("prototypes")] - public List Contents = new(); + [DataField] + public List Prototypes = new(); // TODO: This offset is liable for deprecation. + // TODO: Target tile via code instead? /// /// The offset the prototypes will spawn in on relative to the one prior. /// Set to 0,0 to have them spawn on the same tile. /// - [DataField("offset")] + [DataField] public Vector2 Offset; /// /// Lifetime to set for the entities to self delete /// - [DataField("lifetime")] public float? Lifetime; + [DataField] + public float? Lifetime; - [DataField("speech")] + [DataField] public string? Speech { get; private set; } } diff --git a/Content.Shared/Magic/MagicInstantSpawnData.cs b/Content.Shared/Magic/MagicInstantSpawnData.cs new file mode 100644 index 00000000000..5dcc1453edc --- /dev/null +++ b/Content.Shared/Magic/MagicInstantSpawnData.cs @@ -0,0 +1,25 @@ +namespace Content.Shared.Magic; + +// TODO: If still needed, move to magic component +[ImplicitDataDefinitionForInheritors] +public abstract partial class MagicInstantSpawnData; + +/// +/// Spawns underneath caster. +/// +public sealed partial class TargetCasterPos : MagicInstantSpawnData; + +/// +/// Spawns 3 tiles wide in front of the caster. +/// +public sealed partial class TargetInFront : MagicInstantSpawnData +{ + [DataField] + public int Width = 3; +} + + +/// +/// Spawns 1 tile in front of caster +/// +public sealed partial class TargetInFrontSingle : MagicInstantSpawnData; diff --git a/Content.Shared/Magic/MagicSpawnData.cs b/Content.Shared/Magic/MagicSpawnData.cs deleted file mode 100644 index cd96d4ad76b..00000000000 --- a/Content.Shared/Magic/MagicSpawnData.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Content.Shared.Magic; - -[ImplicitDataDefinitionForInheritors] -public abstract partial class MagicSpawnData -{ - -} - -/// -/// Spawns 1 at the caster's feet. -/// -public sealed partial class TargetCasterPos : MagicSpawnData {} - -/// -/// Targets the 3 tiles in front of the caster. -/// -public sealed partial class TargetInFront : MagicSpawnData -{ - [DataField("width")] public int Width = 3; -} diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs new file mode 100644 index 00000000000..cc7a297aa40 --- /dev/null +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -0,0 +1,519 @@ +using System.Numerics; +using Content.Shared.Actions; +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; +using Content.Shared.Coordinates.Helpers; +using Content.Shared.Doors.Components; +using Content.Shared.Doors.Systems; +using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Interaction; +using Content.Shared.Inventory; +using Content.Shared.Lock; +using Content.Shared.Magic.Components; +using Content.Shared.Magic.Events; +using Content.Shared.Maps; +using Content.Shared.Physics; +using Content.Shared.Popups; +using Content.Shared.Speech.Muting; +using Content.Shared.Storage; +using Content.Shared.Tag; +using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Network; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Random; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Spawners; + +namespace Content.Shared.Magic; + +/// +/// Handles learning and using spells (actions) +/// +public abstract class SharedMagicSystem : EntitySystem +{ + [Dependency] private readonly ISerializationManager _seriMan = default!; + [Dependency] private readonly IComponentFactory _compFact = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedGunSystem _gunSystem = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly SharedBodySystem _body = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedDoorSystem _door = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly LockSystem _lock = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly TagSystem _tag = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnBeforeCastSpell); + + SubscribeLocalEvent(OnInstantSpawn); + SubscribeLocalEvent(OnTeleportSpell); + SubscribeLocalEvent(OnWorldSpawn); + SubscribeLocalEvent(OnProjectileSpell); + SubscribeLocalEvent(OnChangeComponentsSpell); + SubscribeLocalEvent(OnSmiteSpell); + SubscribeLocalEvent(OnKnockSpell); + SubscribeLocalEvent(OnChargeSpell); + + // Spell wishlist + // A wishlish of spells that I'd like to implement or planning on implementing in a future PR + + // TODO: InstantDoAfterSpell and WorldDoafterSpell + // Both would be an action that take in an event, that passes an event to trigger once the doafter is done + // This would be three events: + // 1 - Event that triggers from the action that starts the doafter + // 2 - The doafter event itself, which passes the event with it + // 3 - The event to trigger once the do-after finishes + + // TODO: Inanimate objects to life ECS + // AI sentience + + // TODO: Flesh2Stone + // Entity Target spell + // Synergy with Inanimate object to life (detects player and allows player to move around) + + // TODO: Lightning Spell + // Should just fire lightning, try to prevent arc back to caster + + // TODO: Magic Missile (homing projectile ecs) + // Instant action, target any player (except self) on screen + + // TODO: Random projectile ECS for magic-carp, wand of magic + + // TODO: Recall Spell + // mark any item in hand to recall + // ItemRecallComponent + // Event adds the component if it doesn't exist and the performer isn't stored in the comp + // 2nd firing of the event checks to see if the recall comp has this uid, and if it does it calls it + // if no free hands, summon at feet + // if item deleted, clear stored item + + // TODO: Jaunt (should be its own ECS) + // Instant action + // When clicked, disappear/reappear (goes to paused map) + // option to restrict to tiles + // option for requiring entry/exit (blood jaunt) + // speed option + + // TODO: Summon Events + // List of wizard events to add into the event pool that frequently activate + // floor is lava + // change places + // ECS that when triggered, will periodically trigger a random GameRule + // Would need a controller/controller entity? + + // TODO: Summon Guns + // Summon a random gun at peoples feet + // Get every alive player (not in cryo, not a simplemob) + // TODO: After Antag Rework - Rare chance of giving gun collector status to people + + // TODO: Summon Magic + // Summon a random magic wand at peoples feet + // Get every alive player (not in cryo, not a simplemob) + // TODO: After Antag Rework - Rare chance of giving magic collector status to people + + // TODO: Bottle of Blood + // Summons Slaughter Demon + // TODO: Slaughter Demon + // Also see Jaunt + + // TODO: Field Spells + // Should be able to specify a grid of tiles (3x3 for example) that it effects + // Timed despawn - so it doesn't last forever + // Ignore caster - for spells that shouldn't effect the caster (ie if timestop should effect the caster) + + // TODO: Touch toggle spell + // 1 - When toggled on, show in hand + // 2 - Block hand when toggled on + // - Require free hand + // 3 - use spell event when toggled & click + } + + private void OnBeforeCastSpell(Entity ent, ref BeforeCastSpellEvent args) + { + var comp = ent.Comp; + var hasReqs = true; + + if (comp.RequiresClothes) + { + var enumerator = _inventory.GetSlotEnumerator(args.Performer, SlotFlags.OUTERCLOTHING | SlotFlags.HEAD); + while (enumerator.MoveNext(out var containerSlot)) + { + if (containerSlot.ContainedEntity is { } item) + hasReqs = HasComp(item); + else + hasReqs = false; + + if (!hasReqs) + break; + } + } + + if (comp.RequiresSpeech && HasComp(args.Performer)) + hasReqs = false; + + if (hasReqs) + return; + + args.Cancelled = true; + _popup.PopupClient(Loc.GetString("spell-requirements-failed"), args.Performer, args.Performer); + + // TODO: Pre-cast do after, either here or in SharedActionsSystem + } + + private bool PassesSpellPrerequisites(EntityUid spell, EntityUid performer) + { + var ev = new BeforeCastSpellEvent(performer); + RaiseLocalEvent(spell, ref ev); + return !ev.Cancelled; + } + + #region Spells + #region Instant Spawn Spells + /// + /// Handles the instant action (i.e. on the caster) attempting to spawn an entity. + /// + private void OnInstantSpawn(InstantSpawnSpellEvent args) + { + if (args.Handled || !PassesSpellPrerequisites(args.Action, args.Performer)) + return; + + var transform = Transform(args.Performer); + + foreach (var position in GetInstantSpawnPositions(transform, args.PosData)) + { + SpawnSpellHelper(args.Prototype, position, args.Performer, preventCollide: args.PreventCollideWithCaster); + } + + Speak(args); + args.Handled = true; + } + + /// + /// Gets spawn positions listed on + /// + /// + private List GetInstantSpawnPositions(TransformComponent casterXform, MagicInstantSpawnData data) + { + switch (data) + { + case TargetCasterPos: + return new List(1) {casterXform.Coordinates}; + case TargetInFrontSingle: + { + var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized()); + + if (!TryComp(casterXform.GridUid, out var mapGrid)) + return new List(); + if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager)) + return new List(); + + var tileIndex = tileReference.Value.GridIndices; + return new List(1) { _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex) }; + } + case TargetInFront: + { + var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized()); + + if (!TryComp(casterXform.GridUid, out var mapGrid)) + return new List(); + + if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager)) + return new List(); + + var tileIndex = tileReference.Value.GridIndices; + var coords = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex); + EntityCoordinates coordsPlus; + EntityCoordinates coordsMinus; + + var dir = casterXform.LocalRotation.GetCardinalDir(); + switch (dir) + { + case Direction.North: + case Direction.South: + { + coordsPlus = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex + (1, 0)); + coordsMinus = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex + (-1, 0)); + return new List(3) + { + coords, + coordsPlus, + coordsMinus, + }; + } + case Direction.East: + case Direction.West: + { + coordsPlus = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex + (0, 1)); + coordsMinus = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex + (0, -1)); + return new List(3) + { + coords, + coordsPlus, + coordsMinus, + }; + } + } + + return new List(); + } + default: + throw new ArgumentOutOfRangeException(); + } + } + // End Instant Spawn Spells + #endregion + #region World Spawn Spells + /// + /// Spawns entities from a list within range of click. + /// + /// + /// It will offset entities after the first entity based on the OffsetVector2. + /// + /// The Spawn Spell Event args. + private void OnWorldSpawn(WorldSpawnSpellEvent args) + { + if (args.Handled || !PassesSpellPrerequisites(args.Action, args.Performer)) + return; + + var targetMapCoords = args.Target; + + WorldSpawnSpellHelper(args.Prototypes, targetMapCoords, args.Performer, args.Lifetime, args.Offset); + Speak(args); + args.Handled = true; + } + + /// + /// Loops through a supplied list of entity prototypes and spawns them + /// + /// + /// If an offset of 0, 0 is supplied then the entities will all spawn on the same tile. + /// Any other offset will spawn entities starting from the source Map Coordinates and will increment the supplied + /// offset + /// + /// The list of Entities to spawn in + /// Map Coordinates where the entities will spawn + /// Check to see if the entities should self delete + /// A Vector2 offset that the entities will spawn in + private void WorldSpawnSpellHelper(List entityEntries, EntityCoordinates entityCoords, EntityUid performer, float? lifetime, Vector2 offsetVector2) + { + var getProtos = EntitySpawnCollection.GetSpawns(entityEntries, _random); + + var offsetCoords = entityCoords; + foreach (var proto in getProtos) + { + SpawnSpellHelper(proto, offsetCoords, performer, lifetime); + offsetCoords = offsetCoords.Offset(offsetVector2); + } + } + // End World Spawn Spells + #endregion + #region Projectile Spells + private void OnProjectileSpell(ProjectileSpellEvent ev) + { + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer) || !_net.IsServer) + return; + + ev.Handled = true; + Speak(ev); + + var xform = Transform(ev.Performer); + var fromCoords = xform.Coordinates; + var toCoords = ev.Target; + var userVelocity = _physics.GetMapLinearVelocity(ev.Performer); + + // If applicable, this ensures the projectile is parented to grid on spawn, instead of the map. + var fromMap = fromCoords.ToMap(EntityManager, _transform); + var spawnCoords = _mapManager.TryFindGridAt(fromMap, out var gridUid, out _) + ? fromCoords.WithEntityId(gridUid, EntityManager) + : new(_mapManager.GetMapEntityId(fromMap.MapId), fromMap.Position); + + var ent = Spawn(ev.Prototype, spawnCoords); + var direction = toCoords.ToMapPos(EntityManager, _transform) - + spawnCoords.ToMapPos(EntityManager, _transform); + _gunSystem.ShootProjectile(ent, direction, userVelocity, ev.Performer, ev.Performer); + } + // End Projectile Spells + #endregion + #region Change Component Spells + // staves.yml ActionRGB light + private void OnChangeComponentsSpell(ChangeComponentsSpellEvent ev) + { + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer)) + return; + + ev.Handled = true; + Speak(ev); + + foreach (var toRemove in ev.ToRemove) + { + if (_compFact.TryGetRegistration(toRemove, out var registration)) + RemComp(ev.Target, registration.Type); + } + + foreach (var (name, data) in ev.ToAdd) + { + if (HasComp(ev.Target, data.Component.GetType())) + continue; + + var component = (Component) _compFact.GetComponent(name); + component.Owner = ev.Target; + var temp = (object) component; + _seriMan.CopyTo(data.Component, ref temp); + EntityManager.AddComponent(ev.Target, (Component) temp!); + } + } + // End Change Component Spells + #endregion + #region Teleport Spells + // TODO: Rename to teleport clicked spell? + /// + /// Teleports the user to the clicked location + /// + /// + private void OnTeleportSpell(TeleportSpellEvent args) + { + if (args.Handled || !PassesSpellPrerequisites(args.Action, args.Performer)) + return; + + var transform = Transform(args.Performer); + + if (transform.MapID != args.Target.GetMapId(EntityManager) || !_interaction.InRangeUnobstructed(args.Performer, args.Target, range: 1000F, collisionMask: CollisionGroup.Opaque, popup: true)) + return; + + _transform.SetCoordinates(args.Performer, args.Target); + _transform.AttachToGridOrMap(args.Performer, transform); + Speak(args); + args.Handled = true; + } + // End Teleport Spells + #endregion + #region Spell Helpers + private void SpawnSpellHelper(string? proto, EntityCoordinates position, EntityUid performer, float? lifetime = null, bool preventCollide = false) + { + if (!_net.IsServer) + return; + + var ent = Spawn(proto, position.SnapToGrid(EntityManager, _mapManager)); + + if (lifetime != null) + { + var comp = EnsureComp(ent); + comp.Lifetime = lifetime.Value; + } + + if (preventCollide) + { + var comp = EnsureComp(ent); + comp.Uid = performer; + } + } + // End Spell Helpers + #endregion + #region Smite Spells + private void OnSmiteSpell(SmiteSpellEvent ev) + { + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer)) + return; + + ev.Handled = true; + Speak(ev); + + var direction = _transform.GetMapCoordinates(ev.Target, Transform(ev.Target)).Position - _transform.GetMapCoordinates(ev.Performer, Transform(ev.Performer)).Position; + var impulseVector = direction * 10000; + + _physics.ApplyLinearImpulse(ev.Target, impulseVector); + + if (!TryComp(ev.Target, out var body)) + return; + + _body.GibBody(ev.Target, true, body); + } + // End Smite Spells + #endregion + #region Knock Spells + /// + /// Opens all doors and locks within range + /// + /// + private void OnKnockSpell(KnockSpellEvent args) + { + if (args.Handled || !PassesSpellPrerequisites(args.Action, args.Performer)) + return; + + args.Handled = true; + Speak(args); + + var transform = Transform(args.Performer); + + // Look for doors and lockers, and don't open/unlock them if they're already opened/unlocked. + foreach (var target in _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(args.Performer, transform), args.Range, flags: LookupFlags.Dynamic | LookupFlags.Static)) + { + if (!_interaction.InRangeUnobstructed(args.Performer, target, range: 0, collisionMask: CollisionGroup.Opaque)) + continue; + + if (TryComp(target, out var doorBoltComp) && doorBoltComp.BoltsDown) + _door.SetBoltsDown((target, doorBoltComp), false, predicted: true); + + if (TryComp(target, out var doorComp) && doorComp.State is not DoorState.Open) + _door.StartOpening(target); + + if (TryComp(target, out var lockComp) && lockComp.Locked) + _lock.Unlock(target, args.Performer, lockComp); + } + } + // End Knock Spells + #endregion + #region Charge Spells + // TODO: Future support to charge other items + private void OnChargeSpell(ChargeSpellEvent ev) + { + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer) || !TryComp(ev.Performer, out var handsComp)) + return; + + EntityUid? wand = null; + foreach (var item in _hands.EnumerateHeld(ev.Performer, handsComp)) + { + if (!_tag.HasTag(item, ev.WandTag)) + continue; + + wand = item; + } + + ev.Handled = true; + Speak(ev); + + if (wand == null || !TryComp(wand, out var basicAmmoComp) || basicAmmoComp.Count == null) + return; + + _gunSystem.UpdateBasicEntityAmmoCount(wand.Value, basicAmmoComp.Count.Value + ev.Charge, basicAmmoComp); + } + // End Charge Spells + #endregion + // End Spells + #endregion + + // When any spell is cast it will raise this as an event, so then it can be played in server or something. At least until chat gets moved to shared + // TODO: Temp until chat is in shared + private void Speak(BaseActionEvent args) + { + if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech)) + return; + + var ev = new SpeakSpellEvent(args.Performer, speak.Speech); + RaiseLocalEvent(ref ev); + } +} diff --git a/Content.Shared/Magic/SpellbookSystem.cs b/Content.Shared/Magic/SpellbookSystem.cs new file mode 100644 index 00000000000..84b2b232980 --- /dev/null +++ b/Content.Shared/Magic/SpellbookSystem.cs @@ -0,0 +1,96 @@ +using Content.Shared.Actions; +using Content.Shared.DoAfter; +using Content.Shared.Interaction.Events; +using Content.Shared.Magic.Components; +using Content.Shared.Mind; +using Robust.Shared.Network; + +namespace Content.Shared.Magic; + +public sealed class SpellbookSystem : EntitySystem +{ + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly INetManager _netManager = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnInit, before: [typeof(SharedMagicSystem)]); + SubscribeLocalEvent(OnUse); + SubscribeLocalEvent(OnDoAfter); + } + + private void OnInit(Entity ent, ref MapInitEvent args) + { + foreach (var (id, charges) in ent.Comp.SpellActions) + { + var spell = _actionContainer.AddAction(ent, id); + if (spell == null) + continue; + + int? charge = charges; + if (_actions.GetCharges(spell) != null) + charge = _actions.GetCharges(spell); + + _actions.SetCharges(spell, charge < 0 ? null : charge); + ent.Comp.Spells.Add(spell.Value); + } + } + + private void OnUse(Entity ent, ref UseInHandEvent args) + { + if (args.Handled) + return; + + AttemptLearn(ent, args); + + args.Handled = true; + } + + private void OnDoAfter(Entity ent, ref T args) where T : DoAfterEvent // Sometimes i despise this language + { + if (args.Handled || args.Cancelled) + return; + + args.Handled = true; + + if (!ent.Comp.LearnPermanently) + { + _actions.GrantActions(args.Args.User, ent.Comp.Spells, ent); + return; + } + + if (_mind.TryGetMind(args.Args.User, out var mindId, out _)) + { + var mindActionContainerComp = EnsureComp(mindId); + + if (_netManager.IsServer) + _actionContainer.TransferAllActionsWithNewAttached(ent, mindId, args.Args.User, newContainer: mindActionContainerComp); + } + else + { + foreach (var (id, charges) in ent.Comp.SpellActions) + { + EntityUid? actionId = null; + if (_actions.AddAction(args.Args.User, ref actionId, id)) + _actions.SetCharges(actionId, charges < 0 ? null : charges); + } + } + + ent.Comp.SpellActions.Clear(); + } + + private void AttemptLearn(Entity ent, UseInHandEvent args) + { + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.LearnTime, new SpellbookDoAfterEvent(), ent, target: ent) + { + BreakOnMove = true, + BreakOnDamage = true, + NeedHand = true //What, are you going to read with your eyes only?? + }; + + _doAfter.TryStartDoAfter(doAfterEventArgs); + } +} diff --git a/Content.Shared/Store/ListingPrototype.cs b/Content.Shared/Store/ListingPrototype.cs index d3d2e13cdfd..559c2a33bf5 100644 --- a/Content.Shared/Store/ListingPrototype.cs +++ b/Content.Shared/Store/ListingPrototype.cs @@ -75,14 +75,14 @@ public partial class ListingData : IEquatable, ICloneable public EntProtoId? ProductAction; /// - /// The listing ID of the related upgrade listing. Can be used to link a to an - /// upgrade or to use standalone as an upgrade + /// The listing ID of the related upgrade listing. Can be used to link a to an + /// upgrade or to use standalone as an upgrade /// [DataField] - public ProtoId? ProductUpgradeID; + public ProtoId? ProductUpgradeId; /// - /// Keeps track of the current action entity this is tied to, for action upgrades + /// Keeps track of the current action entity this is tied to, for action upgrades /// [DataField] [NonSerialized] @@ -161,7 +161,7 @@ public object Clone() Priority = Priority, ProductEntity = ProductEntity, ProductAction = ProductAction, - ProductUpgradeID = ProductUpgradeID, + ProductUpgradeId = ProductUpgradeId, ProductActionEntity = ProductActionEntity, ProductEvent = ProductEvent, PurchaseAmount = PurchaseAmount, diff --git a/Resources/Locale/en-US/magic/magic.ftl b/Resources/Locale/en-US/magic/magic.ftl new file mode 100644 index 00000000000..4c8a5fc51d3 --- /dev/null +++ b/Resources/Locale/en-US/magic/magic.ftl @@ -0,0 +1 @@ +spell-requirements-failed = Missing requirements to cast this spell! diff --git a/Resources/Locale/en-US/store/categories.ftl b/Resources/Locale/en-US/store/categories.ftl index 17247b84f49..4ebeff3b237 100644 --- a/Resources/Locale/en-US/store/categories.ftl +++ b/Resources/Locale/en-US/store/categories.ftl @@ -15,3 +15,11 @@ store-category-pointless = Pointless # Revenant store-category-abilities = Abilities + +# Wizard +store-caregory-spellbook-offensive = Offensive Spells +store-caregory-spellbook-defensive = Defensive Spells +store-caregory-spellbook-utility = Utility Spells +store-caregory-spellbook-equipment = Wizard Equipment +store-caregory-spellbook-events = Event Spells + diff --git a/Resources/Locale/en-US/store/currency.ftl b/Resources/Locale/en-US/store/currency.ftl index ed28391531a..ada70b5597a 100644 --- a/Resources/Locale/en-US/store/currency.ftl +++ b/Resources/Locale/en-US/store/currency.ftl @@ -9,3 +9,4 @@ store-currency-display-debugdollar = {$amount -> } store-currency-display-telecrystal = TC store-currency-display-stolen-essence = Stolen Essence +store-currency-display-wizcoin = Wiz€oin™ diff --git a/Resources/Locale/en-US/store/spellbook-catalog.ftl b/Resources/Locale/en-US/store/spellbook-catalog.ftl new file mode 100644 index 00000000000..457f02916f9 --- /dev/null +++ b/Resources/Locale/en-US/store/spellbook-catalog.ftl @@ -0,0 +1,35 @@ +# Spells +spellbook-fireball-name = Fireball +spellbook-fireball-desc = Get most crew exploding with rage when they see this fireball heading toward them! + +spellbook-blink-name = Blink +spellbook-blink-desc = Don't blink or you'll miss yourself teleporting away. + +spellbook-force-wall-name = Force Wall +spellbook-force-wall-desc = Make three walls of pure force that you can pass through, but other's can't. + +spellbook-polymoprh-spider-name = Spider Polymoprh +spellbook-polymorph-spider-desc = Transforms you into a spider, man! + +spellbook-polymorph-rod-name = Rod Polymorph +spellbook-polymorph-rod-desc = Change into an Immovable Rod with limited movement. + +spellbook-charge-name = Charge +spellbook-charge-desc = Adds a charge back to your wand! + +# Equipment + +spellbook-wand-polymorph-door-name = Wand of Entrance +spellbook-wand-polymorph-door-description = For when you need a get-away route. + +spellbook-wand-polymorph-carp-name = Wand of Carp Polymorph +spellbook-wand-polymorph-carp-description = For when you need a carp filet quick and the clown is looking juicy. + +# Events + +spellbook-event-summon-ghosts-name = Summon Ghosts +spellbook-event-summon-ghosts-description = Who ya gonna call? + +# Upgrades +spellbook-upgrade-fireball-name = Upgrade Fireball +spellbook-upgrade-fireball-description = Upgrades Fireball to a maximum of level 3! diff --git a/Resources/Prototypes/Actions/polymorph.yml b/Resources/Prototypes/Actions/polymorph.yml index 7472fc00620..445dc8d9f54 100644 --- a/Resources/Prototypes/Actions/polymorph.yml +++ b/Resources/Prototypes/Actions/polymorph.yml @@ -14,3 +14,33 @@ - type: InstantAction event: !type:PolymorphActionEvent itemIconStyle: NoItem + +- type: entity + id: ActionPolymorphWizardSpider + name: Spider Polymorph + description: Polymorphs you into a Spider. + noSpawn: true + components: + - type: InstantAction + useDelay: 60 + event: !type:PolymorphActionEvent + protoId: WizardSpider + itemIconStyle: NoItem + icon: + sprite: Mobs/Animals/spider.rsi + state: tarantula + +- type: entity + id: ActionPolymorphWizardRod + name: Rod Form + description: CLANG! + noSpawn: true + components: + - type: InstantAction + useDelay: 60 + event: !type:PolymorphActionEvent + protoId: WizardRod + itemIconStyle: NoItem + icon: + sprite: Objects/Fun/immovable_rod.rsi + state: icon diff --git a/Resources/Prototypes/Catalog/spellbook_catalog.yml b/Resources/Prototypes/Catalog/spellbook_catalog.yml new file mode 100644 index 00000000000..38b95c3273c --- /dev/null +++ b/Resources/Prototypes/Catalog/spellbook_catalog.yml @@ -0,0 +1,140 @@ +# Offensive +- type: listing + id: SpellbookFireball + name: spellbook-fireball-name + description: spellbook-fireball-desc + productAction: ActionFireball + productUpgradeId: SpellbookFireballUpgrade + cost: + WizCoin: 2 + categories: + - SpellbookOffensive + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: SpellbookRodForm + name: spellbook-polymorph-rod-name + description: spellbook-polymorph-rod-desc + productAction: ActionPolymorphWizardRod + cost: + WizCoin: 3 + categories: + - SpellbookOffensive + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +# Defensive +- type: listing + id: SpellbookForceWall + name: spellbook-force-wall-name + description: spellbook-force-wall-desc + productAction: ActionForceWall + cost: + WizCoin: 3 + categories: + - SpellbookDefensive + +# Utility +- type: listing + id: SpellbookPolymorphSpider + name: spellbook-polymoprh-spider-name + description: spellbook-polymorph-spider-desc + productAction: ActionPolymorphWizardSpider + cost: + WizCoin: 2 + categories: + - SpellbookUtility + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: SpellbookBlink + name: spellbook-blink-name + description: spellbook-blink-desc + productAction: ActionBlink + cost: + WizCoin: 1 + categories: + - SpellbookUtility + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: SpellbookCharge + name: spellbook-charge-name + description: spellbook-charge-desc + productAction: ActionChargeSpell + cost: + WizCoin: 1 + categories: + - SpellbookUtility + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +# Equipment +- type: listing + id: SpellbookWandDoor + name: spellbook-wand-polymorph-door-name + description: spellbook-wand-polymorph-door-description + productEntity: WeaponWandPolymorphDoor + cost: + WizCoin: 3 + categories: + - SpellbookEquipment + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +- type: listing + id: SpellbookWandPolymorphCarp + name: spellbook-wand-polymorph-carp-name + description: spellbook-wand-polymorph-carp-description + productEntity: WeaponWandPolymorphCarp + cost: + WizCoin: 3 + categories: + - SpellbookEquipment + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +# Event +- type: listing + id: SpellbookEventSummonGhosts + name: spellbook-event-summon-ghosts-name + description: spellbook-event-summon-ghosts-description + productAction: ActionSummonGhosts + cost: + WizCoin: 0 + categories: + - SpellbookEvents + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + +# Upgrades +- type: listing + id: SpellbookFireballUpgrade + productUpgradeId: SpellbookFireballUpgrade + name: spellbook-upgrade-fireball-name + description: spellbook-upgrade-fireball-description + icon: + sprite: Objects/Magic/magicactions.rsi + state: fireball + cost: + WizCoin: 2 + categories: + - SpellbookOffensive + conditions: + - !type:BuyBeforeCondition + whitelist: + - SpellbookFireball + # manual for now + - !type:ListingLimitedStockCondition + stock: 2 diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index a7759f701e9..d59d62251cc 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -392,7 +392,7 @@ - Snout - type: entity - parent: ClothingHeadBase + parent: ClothingHeadHatWizardBase id: ClothingHeadHatRedwizard name: red wizard hat description: Strange-looking red hat-wear that most certainly belongs to a real magic user. @@ -512,7 +512,7 @@ - type: entity - parent: ClothingHeadBase + parent: ClothingHeadHatWizardBase id: ClothingHeadHatVioletwizard name: violet wizard hat description: "Strange-looking violet hat-wear that most certainly belongs to a real magic user." @@ -546,6 +546,7 @@ name: witch hat description: A witch hat. components: + - type: WizardClothes #Yes this will count - type: Sprite sprite: Clothing/Head/Hats/witch.rsi - type: Clothing @@ -570,7 +571,14 @@ sprite: Clothing/Head/Hats/wizard_fake.rsi - type: entity + abstract: true parent: ClothingHeadBase + id: ClothingHeadHatWizardBase + components: + - type: WizardClothes + +- type: entity + parent: ClothingHeadHatWizardBase id: ClothingHeadHatWizard name: wizard hat description: Strange-looking blue hat-wear that most certainly belongs to a powerful magic user. diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml index d17fb275c13..dd74c9bdd4b 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml @@ -166,9 +166,16 @@ - type: Clothing sprite: Clothing/OuterClothing/Misc/santa.rsi -# Is this wizard wearing a fanny pack??? - type: entity + abstract: true parent: ClothingOuterEVASuitBase # DeltaV - Make real wizard clothes space proof + id: ClothingOuterWizardBase + components: + - type: WizardClothes + +# Is this wizard wearing a fanny pack??? +- type: entity + parent: ClothingOuterWizardBase id: ClothingOuterWizardViolet name: violet wizard robes description: A bizarre gem-encrusted violet robe that radiates magical energies. @@ -179,7 +186,7 @@ sprite: Clothing/OuterClothing/Misc/violetwizard.rsi - type: entity - parent: ClothingOuterEVASuitBase # DeltaV - Make real wizard clothes space proof + parent: ClothingOuterWizardBase id: ClothingOuterWizard name: wizard robes description: A bizarre gem-encrusted blue robe that radiates magical energies. @@ -190,7 +197,7 @@ sprite: Clothing/OuterClothing/Misc/wizard.rsi - type: entity - parent: ClothingOuterEVASuitBase # DeltaV - Make real wizard clothes space proof + parent: ClothingOuterWizardBase id: ClothingOuterWizardRed name: red wizard robes description: Strange-looking, red, hat-wear that most certainly belongs to a real magic user. diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 758077c8e73..de42f73c5b8 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -2324,6 +2324,18 @@ bloodMaxVolume: 150 bloodReagent: Laughter +- type: entity + name: wizard spider + parent: MobGiantSpider + id: MobGiantSpiderWizard + description: This spider looks a little magical + suffix: Wizard + components: + - type: Accentless + removes: + - type: ReplacementAccent + accent: xeno #let this wizard speak + - type: entity name: possum parent: SimpleMobBase diff --git a/Resources/Prototypes/Entities/Objects/Magic/books.yml b/Resources/Prototypes/Entities/Objects/Magic/books.yml index dfb875f6771..554c5214c19 100644 --- a/Resources/Prototypes/Entities/Objects/Magic/books.yml +++ b/Resources/Prototypes/Entities/Objects/Magic/books.yml @@ -9,7 +9,7 @@ layers: - state: paper_blood - state: cover_strong - color: "#645a5a" + color: "#645a5a" - state: decor_wingette_flat color: "#4d0303" - state: icon_pentagramm @@ -19,13 +19,58 @@ tags: - Spellbook +# For the Wizard Antag +# Do not add discounts or price inflation +- type: entity + id: WizardsGrimoire + name: wizards grimoire + suffix: Wizard + parent: BaseItem + components: + - type: Sprite + sprite: Objects/Misc/books.rsi + layers: + - state: paper_blood + - state: cover_strong + color: "#645a5a" + - state: decor_wingette_flat + color: "#4d0303" + - state: icon_pentagramm + color: "#f7e19f" + - type: UserInterface + interfaces: + enum.StoreUiKey.Key: + type: StoreBoundUserInterface + - type: ActivatableUI + key: enum.StoreUiKey.Key + - type: Store + refundAllowed: true + ownerOnly: true # get your own tome! + preset: StorePresetSpellbook + balance: + WizCoin: 10 # prices are balanced around this 10 point maximum and how strong the spells are + +# Not meant for wizard antag but meant for spawning, so people can't abuse refund if they were given a tome +- type: entity + id: WizardsGrimoireNoRefund + name: wizards grimoire + suffix: Wizard, No Refund + parent: WizardsGrimoire + components: + - type: Store + refundAllowed: false + ownerOnly: true # get your own tome! + preset: StorePresetSpellbook + balance: + WizCoin: 10 # prices are balanced around this 10 point maximum and how strong the spells are + - type: entity id: SpawnSpellbook name: spawn spellbook parent: BaseSpellbook components: - type: Spellbook - spells: + spellActions: ActionSpawnMagicarpSpell: -1 - type: entity @@ -48,7 +93,7 @@ - state: detail_rivets color: gold - type: Spellbook - spells: + spellActions: ActionForceWall: -1 - type: entity @@ -69,7 +114,7 @@ - state: detail_rivets color: gold - type: Spellbook - spells: + spellActions: ActionBlink: -1 - type: entity @@ -92,7 +137,7 @@ color: red - state: overlay_blood - type: Spellbook - spells: + spellActions: ActionSmite: -1 - type: entity @@ -114,7 +159,7 @@ - state: detail_bookmark color: "#98c495" - type: Spellbook - spells: + spellActions: ActionKnock: -1 - type: entity @@ -138,7 +183,7 @@ - state: icon_magic_fireball shader: unshaded - type: Spellbook - spells: + spellActions: ActionFireball: -1 - type: entity @@ -153,7 +198,7 @@ layers: - state: spell_default - type: Spellbook - spells: + spellActions: ActionFlashRune: -1 ActionExplosionRune: -1 ActionIgniteRune: -1 diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml index 1b105e9cae5..bf1e199c7a4 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml @@ -1214,7 +1214,7 @@ name: force wall components: - type: TimedDespawn - lifetime: 20 + lifetime: 12 - type: Tag tags: - Wall diff --git a/Resources/Prototypes/Magic/event_spells.yml b/Resources/Prototypes/Magic/event_spells.yml new file mode 100644 index 00000000000..e59e1b2db88 --- /dev/null +++ b/Resources/Prototypes/Magic/event_spells.yml @@ -0,0 +1,13 @@ +- type: entity + id: ActionSummonGhosts + name: Summon Ghosts + description: Makes all current ghosts permanently invisible + noSpawn: true + components: + - type: InstantAction + useDelay: 120 + itemIconStyle: BigAction + icon: + sprite: Mobs/Ghosts/ghost_human.rsi + state: icon + event: !type:ToggleGhostVisibilityToAllEvent diff --git a/Resources/Prototypes/Magic/knock_spell.yml b/Resources/Prototypes/Magic/knock_spell.yml index f00897d32c7..e2c3dcfd4c7 100644 --- a/Resources/Prototypes/Magic/knock_spell.yml +++ b/Resources/Prototypes/Magic/knock_spell.yml @@ -7,6 +7,8 @@ - type: InstantAction useDelay: 10 itemIconStyle: BigAction + sound: !type:SoundPathSpecifier + path: /Audio/Magic/knock.ogg icon: sprite: Objects/Magic/magicactions.rsi state: knock diff --git a/Resources/Prototypes/Magic/projectile_spells.yml b/Resources/Prototypes/Magic/projectile_spells.yml index 196472fe7b2..b8db7557bba 100644 --- a/Resources/Prototypes/Magic/projectile_spells.yml +++ b/Resources/Prototypes/Magic/projectile_spells.yml @@ -4,10 +4,12 @@ description: Fires an explosive fireball towards the clicked location. noSpawn: true components: + - type: Magic - type: WorldTargetAction useDelay: 15 itemIconStyle: BigAction checkCanAccess: false + raiseOnUser: true range: 60 sound: !type:SoundPathSpecifier path: /Audio/Magic/fireball.ogg @@ -16,25 +18,25 @@ state: fireball event: !type:ProjectileSpellEvent prototype: ProjectileFireball - posData: !type:TargetCasterPos speech: action-speech-spell-fireball - type: ActionUpgrade effectedLevels: 2: ActionFireballII + 3: ActionFireballIII - type: entity id: ActionFireballII parent: ActionFireball name: Fireball II - description: Fire three explosive fireball towards the clicked location. + description: Fires a fireball, but faster! noSpawn: true components: - type: WorldTargetAction - useDelay: 5 - charges: 3 + useDelay: 10 renewCharges: true itemIconStyle: BigAction checkCanAccess: false + raiseOnUser: true range: 60 sound: !type:SoundPathSpecifier path: /Audio/Magic/fireball.ogg @@ -43,5 +45,27 @@ state: fireball event: !type:ProjectileSpellEvent prototype: ProjectileFireball - posData: !type:TargetCasterPos speech: action-speech-spell-fireball + +- type: entity + id: ActionFireballIII + parent: ActionFireball + name: Fireball III + description: The fastest fireball in the west! + noSpawn: true + components: + - type: WorldTargetAction + useDelay: 8 + renewCharges: true + itemIconStyle: BigAction + checkCanAccess: false + raiseOnUser: true + range: 60 + sound: !type:SoundPathSpecifier + path: /Audio/Magic/fireball.ogg + icon: + sprite: Objects/Magic/magicactions.rsi + state: fireball + event: !type:ProjectileSpellEvent + prototype: ProjectileFireball + speech: action-speech-spell-fireball diff --git a/Resources/Prototypes/Magic/teleport_spells.yml b/Resources/Prototypes/Magic/teleport_spells.yml index 30c83891eee..cc89cf8ee0d 100644 --- a/Resources/Prototypes/Magic/teleport_spells.yml +++ b/Resources/Prototypes/Magic/teleport_spells.yml @@ -8,9 +8,11 @@ useDelay: 10 range: 16 # default examine-range. # ^ should probably add better validation that the clicked location is on the users screen somewhere, + sound: !type:SoundPathSpecifier + path: /Audio/Magic/blink.ogg itemIconStyle: BigAction checkCanAccess: false - repeat: true + repeat: false icon: sprite: Objects/Magic/magicactions.rsi state: blink diff --git a/Resources/Prototypes/Magic/utility_spells.yml b/Resources/Prototypes/Magic/utility_spells.yml new file mode 100644 index 00000000000..dccdda37898 --- /dev/null +++ b/Resources/Prototypes/Magic/utility_spells.yml @@ -0,0 +1,15 @@ +- type: entity + id: ActionChargeSpell + name: Charge + description: Adds a charge back to your wand + noSpawn: true + components: + - type: InstantAction + useDelay: 30 + itemIconStyle: BigAction + icon: + sprite: Objects/Weapons/Guns/Basic/wands.rsi + state: nothing + event: !type:ChargeSpellEvent + charge: 1 + speech: DI'RI CEL! diff --git a/Resources/Prototypes/Polymorphs/polymorph.yml b/Resources/Prototypes/Polymorphs/polymorph.yml index 582f69b744e..a1a805c74fc 100644 --- a/Resources/Prototypes/Polymorphs/polymorph.yml +++ b/Resources/Prototypes/Polymorphs/polymorph.yml @@ -175,3 +175,25 @@ revertOnDeath: true revertOnCrit: true duration: 20 + +# Polymorphs for Wizards polymorph self spell +- type: polymorph + id: WizardSpider + configuration: + entity: MobGiantSpiderWizard #Not angry so ghosts can't just take over the wizard + transferName: true + inventory: None + revertOnDeath: true + revertOnCrit: true + +- type: polymorph + id: WizardRod + configuration: + entity: ImmovableRodWizard #CLANG + transferName: true + transferDamage: false + inventory: None + duration: 1 + forced: true + revertOnCrit: false + revertOnDeath: false diff --git a/Resources/Prototypes/Store/categories.yml b/Resources/Prototypes/Store/categories.yml index 6cf641061e9..6bd9756c3e9 100644 --- a/Resources/Prototypes/Store/categories.yml +++ b/Resources/Prototypes/Store/categories.yml @@ -7,6 +7,32 @@ id: Debug2 name: store-category-debug2 +#WIZARD +- type: storeCategory + id: SpellbookOffensive + name: store-caregory-spellbook-offensive + priority: 0 + +- type: storeCategory + id: SpellbookDefensive + name: store-caregory-spellbook-defensive + priority: 1 + +- type: storeCategory + id: SpellbookUtility + name: store-caregory-spellbook-utility + priority: 2 + +- type: storeCategory + id: SpellbookEquipment + name: store-caregory-spellbook-equipment + priority: 3 + +- type: storeCategory + id: SpellbookEvents + name: store-caregory-spellbook-events + priority: 4 + #uplink categoires - type: storeCategory id: UplinkWeaponry diff --git a/Resources/Prototypes/Store/currency.yml b/Resources/Prototypes/Store/currency.yml index 91039a75e6a..b1cff06be2d 100644 --- a/Resources/Prototypes/Store/currency.yml +++ b/Resources/Prototypes/Store/currency.yml @@ -1,7 +1,7 @@ - type: currency id: Telecrystal displayName: store-currency-display-telecrystal - cash: + cash: 1: Telecrystal1 canWithdraw: true @@ -10,7 +10,12 @@ displayName: store-currency-display-stolen-essence canWithdraw: false +- type: currency + id: WizCoin + displayName: store-currency-display-wizcoin + canWithdraw: false + #debug - type: currency id: DebugDollar - displayName: store-currency-display-debugdollar \ No newline at end of file + displayName: store-currency-display-debugdollar diff --git a/Resources/Prototypes/Store/presets.yml b/Resources/Prototypes/Store/presets.yml index 84aa7db5441..166c29fe416 100644 --- a/Resources/Prototypes/Store/presets.yml +++ b/Resources/Prototypes/Store/presets.yml @@ -15,3 +15,15 @@ - UplinkPointless currencyWhitelist: - Telecrystal + +- type: storePreset + id: StorePresetSpellbook + storeName: Spellbook + categories: + - SpellbookOffensive #Fireball, Rod Form + - SpellbookDefensive #Magic Missile, Wall of Force + - SpellbookUtility #Body Swap, Lich, Teleport, Knock, Polymorph + - SpellbookEquipment #Battlemage Robes, Staff of Locker + - SpellbookEvents #Summon Weapons, Summon Ghosts + currencyWhitelist: + - WizCoin From 25ce6c531b12bcd823a9f2b0e9a0fc4daf0c0fcb Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 12 May 2024 09:18:21 +1000 Subject: [PATCH 124/215] Lobby refactor + species loadouts support (#27576) * Vox stuff * Species loadouts and lobby refactor The control flow for lobby is all over the shop so I pulled it all up from the individual controls so now they handle the bare minimum required and LobbyUIController handles the rest. * a * Bulk changes * a * weh * Character import / export * finalise * woops this stuff too * Also datafield exporting * comments * Review --- Content.Client/Entry/EntryPoint.cs | 2 +- Content.Client/IoC/ClientContentIoC.cs | 5 +- .../ClientPreferencesManager.cs | 4 +- .../IClientPreferencesManager.cs | 3 +- Content.Client/Lobby/LobbyState.cs | 106 +- Content.Client/Lobby/LobbyUIController.cs | 319 +++-- .../Lobby/UI/CharacterPickerButton.xaml | 22 + .../Lobby/UI/CharacterPickerButton.xaml.cs | 92 ++ .../UI/CharacterSetupGui.xaml | 4 - .../Lobby/UI/CharacterSetupGui.xaml.cs | 118 ++ .../UI/HighlightedContainer.xaml | 0 .../UI/HighlightedContainer.xaml.cs | 2 +- .../UI/HumanoidProfileEditor.xaml | 76 +- .../UI/HumanoidProfileEditor.xaml.cs | 1106 ++++++++++------- .../UI/Loadouts}/LoadoutContainer.xaml | 0 .../UI/Loadouts}/LoadoutContainer.xaml.cs | 4 +- .../UI/Loadouts}/LoadoutGroupContainer.xaml | 0 .../Loadouts}/LoadoutGroupContainer.xaml.cs | 11 +- .../UI/Loadouts}/LoadoutWindow.xaml | 0 .../UI/Loadouts}/LoadoutWindow.xaml.cs | 21 +- .../UI/LobbyCharacterPreviewPanel.xaml.cs | 22 +- Content.Client/Lobby/UI/LobbyGui.xaml.cs | 9 +- .../Lobby/UI/ObserveWarningWindow.xaml.cs | 25 +- .../Lobby/UI/Roles/RequirementsSelector.xaml | 9 + .../UI/Roles/RequirementsSelector.xaml.cs | 118 ++ .../UI/Roles/TraitPreferenceSelector.xaml | 7 + .../UI/Roles/TraitPreferenceSelector.xaml.cs | 36 + .../Preferences/UI/AntagPreferenceSelector.cs | 41 - .../Preferences/UI/CharacterSetupGui.xaml.cs | 276 ---- .../UI/HumanoidProfileEditor.Random.cs | 23 - .../Preferences/UI/JobPrioritySelector.cs | 46 - .../Preferences/UI/RequirementsSelector.cs | 222 ---- .../Tests/Lobby/CharacterCreationTest.cs | 1 - .../Tests/Preferences/ServerDbSqliteTests.cs | 29 +- Content.Server/Database/ServerDbBase.cs | 5 +- .../Humanoid/HumanoidCharacterAppearance.cs | 86 +- .../Humanoid/HumanoidProfileExport.cs | 19 + .../SharedHumanoidAppearanceSystem.cs | 42 + .../Preferences/HumanoidCharacterProfile.cs | 327 ++--- .../Loadouts/Effects/GroupLoadoutEffect.cs | 4 +- .../Effects/JobRequirementLoadoutEffect.cs | 2 +- .../Loadouts/Effects/LoadoutEffect.cs | 1 + .../Effects/PointsCostLoadoutEffect.cs | 1 + .../Loadouts/Effects/SpeciesLoadoutEffect.cs | 24 +- .../Preferences/Loadouts/Loadout.cs | 5 +- .../Preferences/Loadouts/RoleLoadout.cs | 37 +- .../preferences/ui/character-setup-gui.ftl | 1 - .../ui/humanoid-profile-editor.ftl | 1 + Resources/Prototypes/Species/vox.yml | 2 +- 49 files changed, 1810 insertions(+), 1506 deletions(-) rename Content.Client/{Preferences => Lobby}/ClientPreferencesManager.cs (97%) rename Content.Client/{Preferences => Lobby}/IClientPreferencesManager.cs (92%) create mode 100644 Content.Client/Lobby/UI/CharacterPickerButton.xaml create mode 100644 Content.Client/Lobby/UI/CharacterPickerButton.xaml.cs rename Content.Client/{Preferences => Lobby}/UI/CharacterSetupGui.xaml (91%) create mode 100644 Content.Client/Lobby/UI/CharacterSetupGui.xaml.cs rename Content.Client/{Preferences => Lobby}/UI/HighlightedContainer.xaml (100%) rename Content.Client/{Preferences => Lobby}/UI/HighlightedContainer.xaml.cs (88%) rename Content.Client/{Preferences => Lobby}/UI/HumanoidProfileEditor.xaml (68%) rename Content.Client/{Preferences => Lobby}/UI/HumanoidProfileEditor.xaml.cs (50%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutContainer.xaml (100%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutContainer.xaml.cs (95%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutGroupContainer.xaml (100%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutGroupContainer.xaml.cs (84%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutWindow.xaml (100%) rename Content.Client/{Preferences/UI => Lobby/UI/Loadouts}/LoadoutWindow.xaml.cs (69%) create mode 100644 Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml create mode 100644 Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs create mode 100644 Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml create mode 100644 Content.Client/Lobby/UI/Roles/TraitPreferenceSelector.xaml.cs delete mode 100644 Content.Client/Preferences/UI/AntagPreferenceSelector.cs delete mode 100644 Content.Client/Preferences/UI/CharacterSetupGui.xaml.cs delete mode 100644 Content.Client/Preferences/UI/HumanoidProfileEditor.Random.cs delete mode 100644 Content.Client/Preferences/UI/JobPrioritySelector.cs delete mode 100644 Content.Client/Preferences/UI/RequirementsSelector.cs create mode 100644 Content.Shared/Humanoid/HumanoidProfileExport.cs diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 6abf8da4230..4446256daf8 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -10,10 +10,10 @@ using Content.Client.Input; using Content.Client.IoC; using Content.Client.Launcher; +using Content.Client.Lobby; using Content.Client.MainMenu; using Content.Client.Parallax.Managers; using Content.Client.Players.PlayTimeTracking; -using Content.Client.Preferences; using Content.Client.Radiation.Overlays; using Content.Client.Replay; using Content.Client.Screenshot; diff --git a/Content.Client/IoC/ClientContentIoC.cs b/Content.Client/IoC/ClientContentIoC.cs index 65e95b76f08..4703915ae76 100644 --- a/Content.Client/IoC/ClientContentIoC.cs +++ b/Content.Client/IoC/ClientContentIoC.cs @@ -2,23 +2,20 @@ using Content.Client.Changelog; using Content.Client.Chat.Managers; using Content.Client.Clickable; -using Content.Client.Options; using Content.Client.Eui; using Content.Client.GhostKick; using Content.Client.Info; using Content.Client.Launcher; using Content.Client.Parallax.Managers; using Content.Client.Players.PlayTimeTracking; -using Content.Client.Preferences; using Content.Client.Screenshot; using Content.Client.Fullscreen; using Content.Client.Stylesheets; using Content.Client.Viewport; using Content.Client.Voting; -using Content.Shared.Administration; using Content.Shared.Administration.Logs; -using Content.Shared.Module; using Content.Client.Guidebook; +using Content.Client.Lobby; using Content.Client.Replay; using Content.Shared.Administration.Managers; using Content.Shared.Players.PlayTimeTracking; diff --git a/Content.Client/Preferences/ClientPreferencesManager.cs b/Content.Client/Lobby/ClientPreferencesManager.cs similarity index 97% rename from Content.Client/Preferences/ClientPreferencesManager.cs rename to Content.Client/Lobby/ClientPreferencesManager.cs index 89cee7bf79b..3f01e1a8f67 100644 --- a/Content.Client/Preferences/ClientPreferencesManager.cs +++ b/Content.Client/Lobby/ClientPreferencesManager.cs @@ -2,12 +2,10 @@ using Content.Shared.Preferences; using Robust.Client; using Robust.Client.Player; -using Robust.Shared.Configuration; using Robust.Shared.Network; -using Robust.Shared.Prototypes; using Robust.Shared.Utility; -namespace Content.Client.Preferences +namespace Content.Client.Lobby { /// /// Receives and from the server during the initial diff --git a/Content.Client/Preferences/IClientPreferencesManager.cs b/Content.Client/Lobby/IClientPreferencesManager.cs similarity index 92% rename from Content.Client/Preferences/IClientPreferencesManager.cs rename to Content.Client/Lobby/IClientPreferencesManager.cs index e55d6b600ca..45a770b1621 100644 --- a/Content.Client/Preferences/IClientPreferencesManager.cs +++ b/Content.Client/Lobby/IClientPreferencesManager.cs @@ -1,7 +1,6 @@ -using System; using Content.Shared.Preferences; -namespace Content.Client.Preferences +namespace Content.Client.Lobby { public interface IClientPreferencesManager { diff --git a/Content.Client/Lobby/LobbyState.cs b/Content.Client/Lobby/LobbyState.cs index 91730020a4e..1aabc4ff381 100644 --- a/Content.Client/Lobby/LobbyState.cs +++ b/Content.Client/Lobby/LobbyState.cs @@ -3,8 +3,6 @@ using Content.Client.LateJoin; using Content.Client.Lobby.UI; using Content.Client.Message; -using Content.Client.Preferences; -using Content.Client.Preferences.UI; using Content.Client.UserInterface.Systems.Chat; using Content.Client.Voting; using Robust.Client; @@ -12,8 +10,6 @@ using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; -using Robust.Shared.Configuration; -using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -25,20 +21,15 @@ public sealed class LobbyState : Robust.Client.State.State [Dependency] private readonly IClientConsoleHost _consoleHost = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IResourceCache _resourceCache = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; - [Dependency] private readonly IClientPreferencesManager _preferencesManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IVoteManager _voteManager = default!; - [Dependency] private readonly IConfigurationManager _configurationManager = default!; - - [ViewVariables] private CharacterSetupGui? _characterSetup; private ClientGameTicker _gameTicker = default!; private ContentAudioSystem _contentAudioSystem = default!; protected override Type? LinkedScreenType { get; } = typeof(LobbyGui); - private LobbyGui? _lobby; + public LobbyGui? Lobby; protected override void Startup() { @@ -47,45 +38,23 @@ protected override void Startup() return; } - _lobby = (LobbyGui) _userInterfaceManager.ActiveScreen; + Lobby = (LobbyGui) _userInterfaceManager.ActiveScreen; var chatController = _userInterfaceManager.GetUIController(); _gameTicker = _entityManager.System(); _contentAudioSystem = _entityManager.System(); _contentAudioSystem.LobbySoundtrackChanged += UpdateLobbySoundtrackInfo; - _characterSetup = new CharacterSetupGui(_entityManager, _resourceCache, _preferencesManager, - _prototypeManager, _configurationManager); - LayoutContainer.SetAnchorPreset(_characterSetup, LayoutContainer.LayoutPreset.Wide); - _lobby.CharacterSetupState.AddChild(_characterSetup); chatController.SetMainChat(true); - _voteManager.SetPopupContainer(_lobby.VoteContainer); - - _characterSetup.CloseButton.OnPressed += _ => - { - // Reset sliders etc. - _characterSetup?.UpdateControls(); - - var controller = _userInterfaceManager.GetUIController(); - controller.SetClothes(true); - controller.UpdateProfile(); - _lobby.SwitchState(LobbyGui.LobbyGuiState.Default); - }; - - _characterSetup.SaveButton.OnPressed += _ => - { - _characterSetup.Save(); - _userInterfaceManager.GetUIController().ReloadProfile(); - }; - - LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide); - _lobby.ServerName.Text = _baseClient.GameInfo?.ServerName; //The eye of refactor gazes upon you... + _voteManager.SetPopupContainer(Lobby.VoteContainer); + LayoutContainer.SetAnchorPreset(Lobby, LayoutContainer.LayoutPreset.Wide); + Lobby.ServerName.Text = _baseClient.GameInfo?.ServerName; //The eye of refactor gazes upon you... UpdateLobbyUi(); - _lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed; - _lobby.ReadyButton.OnPressed += OnReadyPressed; - _lobby.ReadyButton.OnToggled += OnReadyToggled; + Lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed; + Lobby.ReadyButton.OnPressed += OnReadyPressed; + Lobby.ReadyButton.OnToggled += OnReadyToggled; _gameTicker.InfoBlobUpdated += UpdateLobbyUi; _gameTicker.LobbyStatusUpdated += LobbyStatusUpdated; @@ -103,20 +72,23 @@ protected override void Shutdown() _voteManager.ClearPopupContainer(); - _lobby!.CharacterPreview.CharacterSetupButton.OnPressed -= OnSetupPressed; - _lobby!.ReadyButton.OnPressed -= OnReadyPressed; - _lobby!.ReadyButton.OnToggled -= OnReadyToggled; + Lobby!.CharacterPreview.CharacterSetupButton.OnPressed -= OnSetupPressed; + Lobby!.ReadyButton.OnPressed -= OnReadyPressed; + Lobby!.ReadyButton.OnToggled -= OnReadyToggled; - _lobby = null; + Lobby = null; + } - _characterSetup?.Dispose(); - _characterSetup = null; + public void SwitchState(LobbyGui.LobbyGuiState state) + { + // Yeah I hate this but LobbyState contains all the badness for now. + Lobby?.SwitchState(state); } private void OnSetupPressed(BaseButton.ButtonEventArgs args) { SetReady(false); - _lobby!.SwitchState(LobbyGui.LobbyGuiState.CharacterSetup); + Lobby?.SwitchState(LobbyGui.LobbyGuiState.CharacterSetup); } private void OnReadyPressed(BaseButton.ButtonEventArgs args) @@ -138,13 +110,13 @@ public override void FrameUpdate(FrameEventArgs e) { if (_gameTicker.IsGameStarted) { - _lobby!.StartTime.Text = string.Empty; + Lobby!.StartTime.Text = string.Empty; var roundTime = _gameTiming.CurTime.Subtract(_gameTicker.RoundStartTimeSpan); - _lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-time", ("hours", roundTime.Hours), ("minutes", roundTime.Minutes)); + Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-time", ("hours", roundTime.Hours), ("minutes", roundTime.Minutes)); return; } - _lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started"); + Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started"); string text; if (_gameTicker.Paused) @@ -153,7 +125,7 @@ public override void FrameUpdate(FrameEventArgs e) } else if (_gameTicker.StartTime < _gameTiming.CurTime) { - _lobby!.StartTime.Text = Loc.GetString("lobby-state-soon"); + Lobby!.StartTime.Text = Loc.GetString("lobby-state-soon"); return; } else @@ -170,7 +142,7 @@ public override void FrameUpdate(FrameEventArgs e) } } - _lobby!.StartTime.Text = Loc.GetString("lobby-state-round-start-countdown-text", ("timeLeft", text)); + Lobby!.StartTime.Text = Loc.GetString("lobby-state-round-start-countdown-text", ("timeLeft", text)); } private void LobbyStatusUpdated() @@ -181,31 +153,31 @@ private void LobbyStatusUpdated() private void LobbyLateJoinStatusUpdated() { - _lobby!.ReadyButton.Disabled = _gameTicker.DisallowedLateJoin; + Lobby!.ReadyButton.Disabled = _gameTicker.DisallowedLateJoin; } private void UpdateLobbyUi() { if (_gameTicker.IsGameStarted) { - _lobby!.ReadyButton.Text = Loc.GetString("lobby-state-ready-button-join-state"); - _lobby!.ReadyButton.ToggleMode = false; - _lobby!.ReadyButton.Pressed = false; - _lobby!.ObserveButton.Disabled = false; + Lobby!.ReadyButton.Text = Loc.GetString("lobby-state-ready-button-join-state"); + Lobby!.ReadyButton.ToggleMode = false; + Lobby!.ReadyButton.Pressed = false; + Lobby!.ObserveButton.Disabled = false; } else { - _lobby!.StartTime.Text = string.Empty; - _lobby!.ReadyButton.Text = Loc.GetString(_lobby!.ReadyButton.Pressed ? "lobby-state-player-status-ready": "lobby-state-player-status-not-ready"); - _lobby!.ReadyButton.ToggleMode = true; - _lobby!.ReadyButton.Disabled = false; - _lobby!.ReadyButton.Pressed = _gameTicker.AreWeReady; - _lobby!.ObserveButton.Disabled = true; + Lobby!.StartTime.Text = string.Empty; + Lobby!.ReadyButton.Text = Loc.GetString(Lobby!.ReadyButton.Pressed ? "lobby-state-player-status-ready": "lobby-state-player-status-not-ready"); + Lobby!.ReadyButton.ToggleMode = true; + Lobby!.ReadyButton.Disabled = false; + Lobby!.ReadyButton.Pressed = _gameTicker.AreWeReady; + Lobby!.ObserveButton.Disabled = true; } if (_gameTicker.ServerInfoBlob != null) { - _lobby!.ServerInfo.SetInfoBlob(_gameTicker.ServerInfoBlob); + Lobby!.ServerInfo.SetInfoBlob(_gameTicker.ServerInfoBlob); } } @@ -213,7 +185,7 @@ private void UpdateLobbySoundtrackInfo(LobbySoundtrackChangedEvent ev) { if (ev.SoundtrackFilename == null) { - _lobby!.LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text")); + Lobby!.LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text")); } else if ( ev.SoundtrackFilename != null @@ -234,7 +206,7 @@ private void UpdateLobbySoundtrackInfo(LobbySoundtrackChangedEvent ev) ("songTitle", title), ("songArtist", artist)); - _lobby!.LobbySong.SetMarkup(markup); + Lobby!.LobbySong.SetMarkup(markup); } } @@ -242,11 +214,11 @@ private void UpdateLobbyBackground() { if (_gameTicker.LobbyBackground != null) { - _lobby!.Background.Texture = _resourceCache.GetResource(_gameTicker.LobbyBackground ); + Lobby!.Background.Texture = _resourceCache.GetResource(_gameTicker.LobbyBackground ); } else { - _lobby!.Background.Texture = null; + Lobby!.Background.Texture = null; } } diff --git a/Content.Client/Lobby/LobbyUIController.cs b/Content.Client/Lobby/LobbyUIController.cs index 9eb259657dc..ae9196c1100 100644 --- a/Content.Client/Lobby/LobbyUIController.cs +++ b/Content.Client/Lobby/LobbyUIController.cs @@ -2,190 +2,292 @@ using Content.Client.Humanoid; using Content.Client.Inventory; using Content.Client.Lobby.UI; -using Content.Client.Preferences; -using Content.Client.Preferences.UI; +using Content.Client.Players.PlayTimeTracking; using Content.Client.Station; +using Content.Shared.CCVar; using Content.Shared.Clothing; using Content.Shared.GameTicking; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Markings; using Content.Shared.Humanoid.Prototypes; using Content.Shared.Preferences; using Content.Shared.Preferences.Loadouts; -using Content.Shared.Preferences.Loadouts.Effects; using Content.Shared.Roles; +using Content.Shared.Traits; +using Robust.Client.Player; +using Robust.Client.ResourceManagement; using Robust.Client.State; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; +using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Utility; namespace Content.Client.Lobby; public sealed class LobbyUIController : UIController, IOnStateEntered, IOnStateExited { [Dependency] private readonly IClientPreferencesManager _preferencesManager = default!; - [Dependency] private readonly IStateManager _stateManager = default!; + [Dependency] private readonly IConfigurationManager _configurationManager = default!; + [Dependency] private readonly IFileDialogManager _dialogManager = default!; + [Dependency] private readonly ILogManager _logManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; + [Dependency] private readonly IStateManager _stateManager = default!; + [Dependency] private readonly JobRequirementsManager _requirements = default!; + [Dependency] private readonly MarkingManager _markings = default!; [UISystemDependency] private readonly HumanoidAppearanceSystem _humanoid = default!; [UISystemDependency] private readonly ClientInventorySystem _inventory = default!; [UISystemDependency] private readonly StationSpawningSystem _spawn = default!; - private LobbyCharacterPreviewPanel? _previewPanel; - - private bool _showClothes = true; - - /* - * Each character profile has its own dummy. There is also a dummy for the lobby screen + character editor - * that is shared too. - */ + private CharacterSetupGui? _characterSetup; + private HumanoidProfileEditor? _profileEditor; /// - /// Preview dummy for role gear. + /// This is the characher preview panel in the chat. This should only update if their character updates. /// - private EntityUid? _previewDummy; + private LobbyCharacterPreviewPanel? PreviewPanel => GetLobbyPreview(); /// - /// If we currently have a job prototype selected. + /// This is the modified profile currently being edited. /// - private JobPrototype? _dummyJob; - - // TODO: Load the species directly and don't update entity ever. - public event Action? PreviewDummyUpdated; + private HumanoidCharacterProfile? EditedProfile => _profileEditor?.Profile; - private HumanoidCharacterProfile? _profile; + private int? EditedSlot => _profileEditor?.CharacterSlot; public override void Initialize() { base.Initialize(); + _prototypeManager.PrototypesReloaded += OnProtoReload; _preferencesManager.OnServerDataLoaded += PreferencesDataLoaded; - } + _requirements.Updated += OnRequirementsUpdated; - private void PreferencesDataLoaded() - { - UpdateProfile(); + _configurationManager.OnValueChanged(CCVars.FlavorText, args => + { + _profileEditor?.RefreshFlavorText(); + }); + + _configurationManager.OnValueChanged(CCVars.GameRoleTimers, args => + { + _profileEditor?.RefreshAntags(); + _profileEditor?.RefreshJobs(); + _profileEditor?.RefreshLoadouts(); + }); } - public void OnStateEntered(LobbyState state) + private LobbyCharacterPreviewPanel? GetLobbyPreview() { + if (_stateManager.CurrentState is LobbyState lobby) + { + return lobby.Lobby?.CharacterPreview; + } + + return null; } - public void OnStateExited(LobbyState state) + private void OnRequirementsUpdated() { - EntityManager.DeleteEntity(_previewDummy); - _previewDummy = null; + if (_profileEditor != null) + { + _profileEditor.RefreshAntags(); + _profileEditor.RefreshJobs(); + } } - public void SetPreviewPanel(LobbyCharacterPreviewPanel? panel) + private void OnProtoReload(PrototypesReloadedEventArgs obj) { - _previewPanel = panel; - ReloadProfile(); + if (_profileEditor != null) + { + if (obj.WasModified()) + { + _profileEditor.RefreshAntags(); + } + + if (obj.WasModified() || + obj.WasModified()) + { + _profileEditor.RefreshJobs(); + } + + if (obj.WasModified() || + obj.WasModified() || + obj.WasModified()) + { + _profileEditor.RefreshLoadouts(); + } + + if (obj.WasModified()) + { + _profileEditor.RefreshSpecies(); + } + + if (obj.WasModified()) + { + _profileEditor.RefreshTraits(); + } + } } - public void SetClothes(bool value) + private void PreferencesDataLoaded() { - if (_showClothes == value) + PreviewPanel?.SetLoaded(true); + + if (_stateManager.CurrentState is not LobbyState) return; - _showClothes = value; - ReloadCharacterUI(); + ReloadCharacterSetup(); } - public void SetDummyJob(JobPrototype? job) + public void OnStateEntered(LobbyState state) { - _dummyJob = job; - ReloadCharacterUI(); + PreviewPanel?.SetLoaded(_preferencesManager.ServerDataLoaded); + ReloadCharacterSetup(); } - /// - /// Updates the character only with the specified profile change. - /// - public void ReloadProfile() + public void OnStateExited(LobbyState state) { - // Test moment - if (_profile == null || _stateManager.CurrentState is not LobbyState) - return; + PreviewPanel?.SetLoaded(false); + _profileEditor?.Dispose(); + _characterSetup?.Dispose(); - // Ignore job clothes and the likes so we don't spam entities out every frame of color changes. - var previewDummy = EnsurePreviewDummy(_profile); - _humanoid.LoadProfile(previewDummy, _profile); + _characterSetup = null; + _profileEditor = null; } /// - /// Updates the currently selected character's preview. + /// Reloads every single character setup control. /// - public void ReloadCharacterUI() + public void ReloadCharacterSetup() { - // Test moment - if (_profile == null || _stateManager.CurrentState is not LobbyState) - return; - - EntityManager.DeleteEntity(_previewDummy); - _previewDummy = null; - _previewDummy = EnsurePreviewDummy(_profile); - _previewPanel?.SetSprite(_previewDummy.Value); - _previewPanel?.SetSummaryText(_profile.Summary); - _humanoid.LoadProfile(_previewDummy.Value, _profile); - - if (_showClothes) - GiveDummyJobClothesLoadout(_previewDummy.Value, _profile); + RefreshLobbyPreview(); + var (characterGui, profileEditor) = EnsureGui(); + characterGui.ReloadCharacterPickers(); + profileEditor.SetProfile( + (HumanoidCharacterProfile?) _preferencesManager.Preferences?.SelectedCharacter, + _preferencesManager.Preferences?.SelectedCharacterIndex); } /// - /// Updates character profile to the default. + /// Refreshes the character preview in the lobby chat. /// - public void UpdateProfile() + private void RefreshLobbyPreview() { - if (!_preferencesManager.ServerDataLoaded) - { - _profile = null; + if (PreviewPanel == null) return; - } - if (_preferencesManager.Preferences?.SelectedCharacter is HumanoidCharacterProfile selectedCharacter) - { - _profile = selectedCharacter; - _previewPanel?.SetLoaded(true); - } - else + // Get selected character, load it, then set it + var character = _preferencesManager.Preferences?.SelectedCharacter; + + if (character is not HumanoidCharacterProfile humanoid) { - _previewPanel?.SetSummaryText(string.Empty); - _previewPanel?.SetLoaded(false); + PreviewPanel.SetSprite(EntityUid.Invalid); + PreviewPanel.SetSummaryText(string.Empty); + return; } - ReloadCharacterUI(); + var dummy = LoadProfileEntity(humanoid, null, true); + PreviewPanel.SetSprite(dummy); + PreviewPanel.SetSummaryText(humanoid.Summary); } - public void UpdateProfile(HumanoidCharacterProfile? profile) + private void SaveProfile() { - if (_profile?.Equals(profile) == true) + DebugTools.Assert(EditedProfile != null); + + if (EditedProfile == null || EditedSlot == null) return; - if (_stateManager.CurrentState is not LobbyState) + var selected = _preferencesManager.Preferences?.SelectedCharacterIndex; + + if (selected == null) return; - _profile = profile; + _preferencesManager.UpdateCharacter(EditedProfile, EditedSlot.Value); + ReloadCharacterSetup(); } - private EntityUid EnsurePreviewDummy(HumanoidCharacterProfile profile) + private (CharacterSetupGui, HumanoidProfileEditor) EnsureGui() { - if (_previewDummy != null) - return _previewDummy.Value; + if (_characterSetup != null && _profileEditor != null) + { + _characterSetup.Visible = true; + _profileEditor.Visible = true; + return (_characterSetup, _profileEditor); + } + + _profileEditor = new HumanoidProfileEditor( + _preferencesManager, + _configurationManager, + EntityManager, + _dialogManager, + _logManager, + _playerManager, + _prototypeManager, + _requirements, + _markings); + + _characterSetup = new CharacterSetupGui(EntityManager, _prototypeManager, _resourceCache, _preferencesManager, _profileEditor); + + _characterSetup.CloseButton.OnPressed += _ => + { + // Reset sliders etc. + _profileEditor.SetProfile(null, null); + _profileEditor.Visible = false; + + if (_stateManager.CurrentState is LobbyState lobbyGui) + { + lobbyGui.SwitchState(LobbyGui.LobbyGuiState.Default); + } + }; + + _profileEditor.Save += SaveProfile; + + _characterSetup.SelectCharacter += args => + { + _preferencesManager.SelectCharacter(args); + ReloadCharacterSetup(); + }; + + _characterSetup.DeleteCharacter += args => + { + _preferencesManager.DeleteCharacter(args); - _previewDummy = EntityManager.SpawnEntity(_prototypeManager.Index(profile.Species).DollPrototype, MapCoordinates.Nullspace); - PreviewDummyUpdated?.Invoke(_previewDummy.Value); - return _previewDummy.Value; + // Reload everything + if (EditedSlot == args) + { + ReloadCharacterSetup(); + } + else + { + // Only need to reload character pickers + _characterSetup?.ReloadCharacterPickers(); + } + }; + + if (_stateManager.CurrentState is LobbyState lobby) + { + lobby.Lobby?.CharacterSetupState.AddChild(_characterSetup); + } + + return (_characterSetup, _profileEditor); } + #region Helpers + /// /// Applies the highest priority job's clothes to the dummy. /// - public void GiveDummyJobClothesLoadout(EntityUid dummy, HumanoidCharacterProfile profile) + public void GiveDummyJobClothesLoadout(EntityUid dummy, JobPrototype? jobProto, HumanoidCharacterProfile profile) { - var job = _dummyJob ?? GetPreferredJob(profile); + var job = jobProto ?? GetPreferredJob(profile); GiveDummyJobClothes(dummy, profile, job); if (_prototypeManager.HasIndex(LoadoutSystem.GetJobPrototype(job.ID))) { - var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), EntityManager, _prototypeManager); + var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), profile.Species, EntityManager, _prototypeManager); GiveDummyLoadout(dummy, loadout); } } @@ -279,8 +381,39 @@ public void GiveDummyJobClothes(EntityUid dummy, HumanoidCharacterProfile profil } } - public EntityUid? GetPreviewDummy() + /// + /// Loads the profile onto a dummy entity. + /// + public EntityUid LoadProfileEntity(HumanoidCharacterProfile? humanoid, JobPrototype? job, bool jobClothes) { - return _previewDummy; + EntityUid dummyEnt; + + if (humanoid is not null) + { + var dummy = _prototypeManager.Index(humanoid.Species).DollPrototype; + dummyEnt = EntityManager.SpawnEntity(dummy, MapCoordinates.Nullspace); + } + else + { + dummyEnt = EntityManager.SpawnEntity(_prototypeManager.Index(SharedHumanoidAppearanceSystem.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace); + } + + _humanoid.LoadProfile(dummyEnt, humanoid); + + if (humanoid != null && jobClothes) + { + job ??= GetPreferredJob(humanoid); + GiveDummyJobClothes(dummyEnt, humanoid, job); + + if (_prototypeManager.HasIndex(LoadoutSystem.GetJobPrototype(job.ID))) + { + var loadout = humanoid.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), humanoid.Species, EntityManager, _prototypeManager); + GiveDummyLoadout(dummyEnt, loadout); + } + } + + return dummyEnt; } + + #endregion } diff --git a/Content.Client/Lobby/UI/CharacterPickerButton.xaml b/Content.Client/Lobby/UI/CharacterPickerButton.xaml new file mode 100644 index 00000000000..af1e640aadb --- /dev/null +++ b/Content.Client/Lobby/UI/CharacterPickerButton.xaml @@ -0,0 +1,22 @@ + + + + + [GenerateTypedNameReferences] + public sealed partial class CharacterSetupGui : Control + { + private readonly IClientPreferencesManager _preferencesManager; + private readonly IEntityManager _entManager; + private readonly IPrototypeManager _protomanager; + + private readonly Button _createNewCharacterButton; + + public event Action? SelectCharacter; + public event Action? DeleteCharacter; + + public CharacterSetupGui( + IEntityManager entManager, + IPrototypeManager protoManager, + IResourceCache resourceCache, + IClientPreferencesManager preferencesManager, + HumanoidProfileEditor profileEditor) + { + RobustXamlLoader.Load(this); + _preferencesManager = preferencesManager; + _entManager = entManager; + _protomanager = protoManager; + + var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); + var back = new StyleBoxTexture + { + Texture = panelTex, + Modulate = new Color(37, 37, 42) + }; + back.SetPatchMargin(StyleBox.Margin.All, 10); + + BackgroundPanel.PanelOverride = back; + + _createNewCharacterButton = new Button + { + Text = Loc.GetString("character-setup-gui-create-new-character-button"), + }; + + _createNewCharacterButton.OnPressed += args => + { + preferencesManager.CreateCharacter(HumanoidCharacterProfile.Random()); + ReloadCharacterPickers(); + args.Event.Handle(); + }; + + CharEditor.AddChild(profileEditor); + RulesButton.OnPressed += _ => new RulesAndInfoWindow().Open(); + + StatsButton.OnPressed += _ => new PlaytimeStatsWindow().OpenCentered(); + } + + /// + /// Disposes and reloads all character picker buttons from the preferences data. + /// + public void ReloadCharacterPickers() + { + _createNewCharacterButton.Orphan(); + Characters.DisposeAllChildren(); + + var numberOfFullSlots = 0; + var characterButtonsGroup = new ButtonGroup(); + + if (!_preferencesManager.ServerDataLoaded) + { + return; + } + + _createNewCharacterButton.ToolTip = + Loc.GetString("character-setup-gui-create-new-character-button-tooltip", + ("maxCharacters", _preferencesManager.Settings!.MaxCharacterSlots)); + + var selectedSlot = _preferencesManager.Preferences?.SelectedCharacterIndex; + + foreach (var (slot, character) in _preferencesManager.Preferences!.Characters) + { + numberOfFullSlots++; + var characterPickerButton = new CharacterPickerButton(_entManager, + _protomanager, + characterButtonsGroup, + character, + slot == selectedSlot); + + Characters.AddChild(characterPickerButton); + + characterPickerButton.OnPressed += args => + { + SelectCharacter?.Invoke(slot); + }; + + characterPickerButton.OnDeletePressed += () => + { + DeleteCharacter?.Invoke(slot); + }; + } + + _createNewCharacterButton.Disabled = numberOfFullSlots >= _preferencesManager.Settings.MaxCharacterSlots; + Characters.AddChild(_createNewCharacterButton); + } + } +} diff --git a/Content.Client/Preferences/UI/HighlightedContainer.xaml b/Content.Client/Lobby/UI/HighlightedContainer.xaml similarity index 100% rename from Content.Client/Preferences/UI/HighlightedContainer.xaml rename to Content.Client/Lobby/UI/HighlightedContainer.xaml diff --git a/Content.Client/Preferences/UI/HighlightedContainer.xaml.cs b/Content.Client/Lobby/UI/HighlightedContainer.xaml.cs similarity index 88% rename from Content.Client/Preferences/UI/HighlightedContainer.xaml.cs rename to Content.Client/Lobby/UI/HighlightedContainer.xaml.cs index 68294d0f059..084c1c37098 100644 --- a/Content.Client/Preferences/UI/HighlightedContainer.xaml.cs +++ b/Content.Client/Lobby/UI/HighlightedContainer.xaml.cs @@ -2,7 +2,7 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; -namespace Content.Client.Preferences.UI; +namespace Content.Client.Lobby.UI; [GenerateTypedNameReferences] public sealed partial class HighlightedContainer : PanelContainer diff --git a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml similarity index 68% rename from Content.Client/Preferences/UI/HumanoidProfileEditor.xaml rename to Content.Client/Lobby/UI/HumanoidProfileEditor.xaml index 5926aee8987..918b6840b45 100644 --- a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml @@ -1,8 +1,8 @@ @@ -10,45 +10,41 @@ - + - + public abstract class SharedHumanoidAppearanceSystem : EntitySystem { + [Dependency] private readonly IConfigurationManager _cfgManager = default!; [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly ISerializationManager _serManager = default!; [Dependency] private readonly MarkingManager _markingManager = default!; [ValidatePrototypeId] @@ -37,6 +48,37 @@ public override void Initialize() SubscribeLocalEvent(OnExamined); } + public DataNode ToDataNode(HumanoidCharacterProfile profile) + { + var export = new HumanoidProfileExport() + { + ForkId = _cfgManager.GetCVar(CVars.BuildForkId), + Profile = profile, + }; + + var dataNode = _serManager.WriteValue(export, alwaysWrite: true, notNullableOverride: true); + return dataNode; + } + + public HumanoidCharacterProfile FromStream(Stream stream, ICommonSession session) + { + using var reader = new StreamReader(stream, EncodingHelpers.UTF8); + var yamlStream = new YamlStream(); + yamlStream.Load(reader); + + var root = yamlStream.Documents[0].RootNode; + var export = _serManager.Read(root.ToDataNode(), notNullableOverride: true); + + /* + * Add custom handling here for forks / version numbers if you care. + */ + + var profile = export.Profile; + var collection = IoCManager.Instance; + profile.EnsureValid(session, collection!); + return profile; + } + private void OnInit(EntityUid uid, HumanoidAppearanceComponent humanoid, ComponentInit args) { if (string.IsNullOrEmpty(humanoid.Species) || _netManager.IsClient && !IsClientSide(uid)) diff --git a/Content.Shared/Preferences/HumanoidCharacterProfile.cs b/Content.Shared/Preferences/HumanoidCharacterProfile.cs index 5017f323b61..6a297bb1f2a 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -5,7 +5,6 @@ using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; using Content.Shared.Preferences.Loadouts; -using Content.Shared.Preferences.Loadouts.Effects; using Content.Shared.Roles; using Content.Shared.Traits; using Robust.Shared.Collections; @@ -41,16 +40,101 @@ public sealed partial class HumanoidCharacterProfile : ICharacterProfile public const int MaxNameLength = 32; public const int MaxDescLength = 512; - private readonly Dictionary _jobPriorities; - private readonly List _antagPreferences; - private readonly List _traitPreferences; + /// + /// Job preferences for initial spawn. + /// + [DataField] + private Dictionary _jobPriorities = new() + { + { + SharedGameTicker.FallbackOverflowJob, JobPriority.High + } + }; + + /// + /// Antags we have opted in to. + /// + [DataField] + private HashSet _antagPreferences = new(); + + /// + /// Enabled traits. + /// + [DataField] + private HashSet _traitPreferences = new(); + /// + /// + /// public IReadOnlyDictionary Loadouts => _loadouts; - private Dictionary _loadouts; + [DataField] + private Dictionary _loadouts = new(); + + [DataField] + public string Name { get; set; } = "John Doe"; + + /// + /// Detailed text that can appear for the character if is enabled. + /// + [DataField] + public string FlavorText { get; set; } = string.Empty; + + /// + /// Associated for this profile. + /// + [DataField] + public string Species { get; set; } = SharedHumanoidAppearanceSystem.DefaultSpecies; + + [DataField] + public int Age { get; set; } = 18; + + [DataField] + public Sex Sex { get; private set; } = Sex.Male; + + [DataField] + public Gender Gender { get; private set; } = Gender.Male; + + /// + /// + /// + public ICharacterAppearance CharacterAppearance => Appearance; - // What in the lord is happening here. - private HumanoidCharacterProfile( + /// + /// Stores markings, eye colors, etc for the profile. + /// + [DataField] + public HumanoidCharacterAppearance Appearance { get; set; } = new(); + + /// + /// When spawning into a round what's the preferred spot to spawn. + /// + [DataField] + public SpawnPriorityPreference SpawnPriority { get; private set; } = SpawnPriorityPreference.None; + + /// + /// + /// + public IReadOnlyDictionary JobPriorities => _jobPriorities; + + /// + /// + /// + public IReadOnlySet AntagPreferences => _antagPreferences; + + /// + /// + /// + public IReadOnlySet TraitPreferences => _traitPreferences; + + /// + /// If we're unable to get one of our preferred jobs do we spawn as a fallback job or do we stay in lobby. + /// + [DataField] + public PreferenceUnavailableMode PreferenceUnavailable { get; private set; } = + PreferenceUnavailableMode.SpawnAsOverflow; + + public HumanoidCharacterProfile( string name, string flavortext, string species, @@ -61,8 +145,8 @@ private HumanoidCharacterProfile( SpawnPriorityPreference spawnPriority, Dictionary jobPriorities, PreferenceUnavailableMode preferenceUnavailable, - List antagPreferences, - List traitPreferences, + HashSet antagPreferences, + HashSet traitPreferences, Dictionary loadouts) { Name = name; @@ -80,40 +164,21 @@ private HumanoidCharacterProfile( _loadouts = loadouts; } - /// Copy constructor but with overridable references (to prevent useless copies) - private HumanoidCharacterProfile( - HumanoidCharacterProfile other, - Dictionary jobPriorities, - List antagPreferences, - List traitPreferences, - Dictionary loadouts) - : this(other.Name, other.FlavorText, other.Species, other.Age, other.Sex, other.Gender, other.Appearance, other.SpawnPriority, - jobPriorities, other.PreferenceUnavailable, antagPreferences, traitPreferences, loadouts) - { - } - /// Copy constructor - private HumanoidCharacterProfile(HumanoidCharacterProfile other) - : this(other, new Dictionary(other.JobPriorities), new List(other.AntagPreferences), new List(other.TraitPreferences), new Dictionary(other.Loadouts)) - { - } - - public HumanoidCharacterProfile( - string name, - string flavortext, - string species, - int age, - Sex sex, - Gender gender, - HumanoidCharacterAppearance appearance, - SpawnPriorityPreference spawnPriority, - IReadOnlyDictionary jobPriorities, - PreferenceUnavailableMode preferenceUnavailable, - IReadOnlyList antagPreferences, - IReadOnlyList traitPreferences, - Dictionary loadouts) - : this(name, flavortext, species, age, sex, gender, appearance, spawnPriority, new Dictionary(jobPriorities), - preferenceUnavailable, new List(antagPreferences), new List(traitPreferences), new Dictionary(loadouts)) + public HumanoidCharacterProfile(HumanoidCharacterProfile other) + : this(other.Name, + other.FlavorText, + other.Species, + other.Age, + other.Sex, + other.Gender, + other.Appearance.Clone(), + other.SpawnPriority, + new Dictionary(other.JobPriorities), + other.PreferenceUnavailable, + new HashSet(other.AntagPreferences), + new HashSet(other.TraitPreferences), + new Dictionary(other.Loadouts)) { } @@ -122,23 +187,7 @@ public HumanoidCharacterProfile( /// Defaults to for the species. ///