diff --git a/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs b/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs new file mode 100644 index 00000000000..5115996c218 --- /dev/null +++ b/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs @@ -0,0 +1,80 @@ +using Content.Shared.CCVar; +using Robust.Shared.Configuration; +using Robust.Shared.Console; + +namespace Content.IntegrationTests.Tests.Commands; + +[TestFixture] +public sealed class ForceMapTest +{ + private const string DefaultMapName = "Empty"; + private const string BadMapName = "asdf_asd-fa__sdfAsd_f"; // Hopefully no one ever names a map this... + private const string TestMapEligibleName = "ForceMapTestEligible"; + private const string TestMapIneligibleName = "ForceMapTestIneligible"; + + [TestPrototypes] + private static readonly string TestMaps = @$" +- type: gameMap + id: {TestMapIneligibleName} + mapName: {TestMapIneligibleName} + mapPath: /Maps/Test/empty.yml + minPlayers: 20 + maxPlayers: 80 + stations: + Empty: + stationProto: StandardNanotrasenStation + components: + - type: StationNameSetup + mapNameTemplate: ""Empty"" + +- type: gameMap + id: {TestMapEligibleName} + mapName: {TestMapEligibleName} + mapPath: /Maps/Test/empty.yml + minPlayers: 0 + stations: + Empty: + stationProto: StandardNanotrasenStation + components: + - type: StationNameSetup + mapNameTemplate: ""Empty"" +"; + + [Test] + public async Task TestForceMapCommand() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entMan = server.EntMan; + var configManager = server.ResolveDependency(); + var consoleHost = server.ResolveDependency(); + + await server.WaitAssertion(() => + { + // Make sure we're set to the default map + Assert.That(configManager.GetCVar(CCVars.GameMap), Is.EqualTo(DefaultMapName), + $"Test didn't start on expected map ({DefaultMapName})!"); + + // Try changing to a map that doesn't exist + consoleHost.ExecuteCommand($"forcemap {BadMapName}"); + Assert.That(configManager.GetCVar(CCVars.GameMap), Is.EqualTo(DefaultMapName), + $"Forcemap succeeded with a map that does not exist ({BadMapName})!"); + + // Try changing to a valid map + consoleHost.ExecuteCommand($"forcemap {TestMapEligibleName}"); + Assert.That(configManager.GetCVar(CCVars.GameMap), Is.EqualTo(TestMapEligibleName), + $"Forcemap failed with a valid map ({TestMapEligibleName})"); + + // Try changing to a map that exists but is ineligible + consoleHost.ExecuteCommand($"forcemap {TestMapIneligibleName}"); + Assert.That(configManager.GetCVar(CCVars.GameMap), Is.EqualTo(TestMapIneligibleName), + $"Forcemap failed with valid but ineligible map ({TestMapIneligibleName})!"); + }); + + // Cleanup + configManager.SetCVar(CCVars.GameMap, DefaultMapName); + + await pair.CleanReturnAsync(); + } +} diff --git a/Content.Server/Connection/GrantConnectBypassCommand.cs b/Content.Server/Connection/GrantConnectBypassCommand.cs index e2d0d7338a4..f611eee04ec 100644 --- a/Content.Server/Connection/GrantConnectBypassCommand.cs +++ b/Content.Server/Connection/GrantConnectBypassCommand.cs @@ -4,7 +4,7 @@ namespace Content.Server.Connection; -[AdminCommand(AdminFlags.Admin)] +[AdminCommand(AdminFlags.Moderator)] public sealed class GrantConnectBypassCommand : LocalizedCommands { private static readonly TimeSpan DefaultDuration = TimeSpan.FromHours(1); diff --git a/Content.Server/GameTicking/Commands/ForceMapCommand.cs b/Content.Server/GameTicking/Commands/ForceMapCommand.cs index 5677ff4b8ff..76777623a2b 100644 --- a/Content.Server/GameTicking/Commands/ForceMapCommand.cs +++ b/Content.Server/GameTicking/Commands/ForceMapCommand.cs @@ -29,9 +29,9 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var gameMap = IoCManager.Resolve(); var name = args[0]; - if (!gameMap.TrySelectMapIfEligible(name)) + if (!gameMap.CheckMapExists(name)) { - shell.WriteLine($"No eligible map exists with name {name}."); + shell.WriteLine(Loc.GetString("forcemap-command-map-not-found", ("map", name))); return; } diff --git a/Content.Server/Repairable/RepairableSystem.cs b/Content.Server/Repairable/RepairableSystem.cs index 2a4d177965d..cbd7efa46db 100644 --- a/Content.Server/Repairable/RepairableSystem.cs +++ b/Content.Server/Repairable/RepairableSystem.cs @@ -3,6 +3,9 @@ using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Interaction; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; using Content.Shared.Popups; using Content.Shared.Repairable; using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem; @@ -15,6 +18,7 @@ public sealed class RepairableSystem : SharedRepairableSystem [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly IAdminLogManager _adminLogger= default!; + [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!; public override void Initialize() { @@ -78,19 +82,30 @@ public async void Repair(EntityUid uid, RepairableComponent component, InteractU return; // Sunrise-end - float delay = component.DoAfterDelay; - // Add a penalty to how long it takes if the user is repairing itself - if (args.User == args.Target) - { - if (!component.AllowSelfRepair) - return; + var isNotSelf = args.User != args.Target; - delay *= component.SelfRepairPenalty; - } + var delay = isNotSelf + ? component.DoAfterDelay + : component.DoAfterDelay * GetScaledRepairPenalty(args.User, component); // Run the repairing doafter args.Handled = _toolSystem.UseTool(args.Used, args.User, uid, delay, component.QualityNeeded, new RepairFinishedEvent(), component.FuelCost); } + + public float GetScaledRepairPenalty(EntityUid uid, RepairableComponent component) + { + var output = component.DoAfterDelay; + if (!TryComp(uid, out var mobThreshold) || + !TryComp(uid, out var damageable)) + return output; + if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var amount, mobThreshold)) + return 1; + + var percentDamage = (float) (damageable.TotalDamage / amount); + //basically make it scale from 1 to the multiplier. + var modifier = percentDamage * (component.SelfRepairPenalty - 1) + 1; + return Math.Max(modifier, 1); + } } } diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 84f31adef45..0e9b0bc32f9 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -330,5 +330,12 @@ Entries: id: 40 time: '2024-06-21T12:06:07.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29258 +- author: Chief-Engineer + changes: + - message: grant_connect_bypass now requires moderator permissions instead of admin. + type: Tweak + id: 41 + time: '2024-06-24T21:56:29.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29406 Name: Admin Order: 1 diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 27fca87738c..e39daa4c1e9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Plykiya - changes: - - message: You can now carefully walk over glass shards and D4. - type: Tweak - id: 6315 - time: '2024-04-06T04:49:14.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26763 - author: PursuitInAshes changes: - message: Sake Bottles can now be found in the booze-o-mat. @@ -3836,3 +3829,11 @@ id: 6814 time: '2024-06-24T15:36:53.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29404 +- author: Emisse + changes: + - message: atmos, elite syndie, and deathsquad hardsuits are now the only fireproof + suits + type: Tweak + id: 6815 + time: '2024-06-24T22:03:05.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29416 diff --git a/Resources/Locale/en-US/game-ticking/forcemap-command.ftl b/Resources/Locale/en-US/game-ticking/forcemap-command.ftl index df2ca687867..6a4399cdcfe 100644 --- a/Resources/Locale/en-US/game-ticking/forcemap-command.ftl +++ b/Resources/Locale/en-US/game-ticking/forcemap-command.ftl @@ -3,5 +3,6 @@ forcemap-command-description = Forces the game to start with a given map next round. forcemap-command-help = forcemap forcemap-command-need-one-argument = forcemap takes one argument, the path to the map file. +forcemap-command-map-not-found = No eligible map exists with name { $map }. forcemap-command-success = Forced the game to start with map { $map } next round. forcemap-command-arg-map = diff --git a/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml b/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml index f9ff59d6b8b..462032547b2 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml @@ -169,8 +169,6 @@ lowPressureMultiplier: 1000 - type: TemperatureProtection coefficient: 0.1 - - type: FireProtection - reduction: 0.2 - type: Armor modifiers: coefficients: @@ -266,4 +264,4 @@ slots: - Hair - HeadTop - - HeadSide \ No newline at end of file + - HeadSide diff --git a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml index 12880a3b356..d43b2fe68cb 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml @@ -61,6 +61,8 @@ lowPressureMultiplier: 1000 - type: TemperatureProtection coefficient: 0.005 + - type: FireProtection + reduction: 0.2 #Engineering Hardsuit - type: entity @@ -692,6 +694,8 @@ - type: PressureProtection highPressureMultiplier: 0.08 lowPressureMultiplier: 1000 + - type: FireProtection + reduction: 0.2 - type: Armor modifiers: coefficients: diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml index c6a3f01f15b..7c264a596a0 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml @@ -110,8 +110,6 @@ lowPressureMultiplier: 1000 - type: TemperatureProtection coefficient: 0.01 - - type: FireProtection - reduction: 0.75 # almost perfectly sealed, atmos firesuit is better - type: ClothingSpeedModifier walkModifier: 0.4 sprintModifier: 0.6 @@ -180,4 +178,4 @@ id: ClothingOuterBaseMedium components: - type: Item - size: Huge \ No newline at end of file + size: Huge diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml index 4655e1d01f3..b73cd51de7e 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml @@ -46,6 +46,8 @@ lowPressureMultiplier: 1000 - type: TemperatureProtection coefficient: 0.001 + - type: FireProtection + reduction: 0.8 - type: ExplosionResistance damageCoefficient: 0.5 - type: Armor @@ -545,7 +547,7 @@ - type: ExplosionResistance damageCoefficient: 0.2 - type: FireProtection - reduction: 0.8 # perfect protection like atmos firesuit for pyro tf2 ops + reduction: 0.8 - type: Armor modifiers: coefficients: @@ -862,6 +864,8 @@ coefficient: 0.001 - type: ExplosionResistance damageCoefficient: 0.2 + - type: FireProtection + reduction: 0.8 - type: Armor modifiers: coefficients: diff --git a/Resources/Prototypes/_Sunrise/Entities/Mobs/Species/synth.yml b/Resources/Prototypes/_Sunrise/Entities/Mobs/Species/synth.yml index 9e89dd1b946..28f4c5e4376 100644 --- a/Resources/Prototypes/_Sunrise/Entities/Mobs/Species/synth.yml +++ b/Resources/Prototypes/_Sunrise/Entities/Mobs/Species/synth.yml @@ -305,7 +305,7 @@ - type: Synth - type: Repairable doAfterDelay: 3 - selfRepairPenalty: 2 + selfRepairPenalty: 3 damage: types: Blunt: -5