Merge remote-tracking branch 'upstream/stable' into ed-30-04-2025-upstream-sync

# Conflicts:
#	Content.Client/Parallax/ParallaxControl.cs
#	Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs
#	Content.IntegrationTests/Tests/PostMapInitTest.cs
#	Content.Server/Chat/Managers/ChatManager.cs
#	Content.Server/Fluids/EntitySystems/PuddleSystem.Evaporation.cs
#	Content.Server/Labels/Label/LabelSystem.cs
#	Content.Shared/Actions/SharedActionsSystem.cs
#	Content.Shared/Fluids/Components/EvaporationComponent.cs
#	Content.Shared/Labels/EntitySystems/SharedLabelSystem.cs
#	README.md
#	Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml
#	Resources/Prototypes/Maps/Pools/deathmatch.yml
#	Resources/Prototypes/Maps/arenas.yml
This commit is contained in:
Ed
2025-04-30 20:31:50 +03:00
2053 changed files with 113995 additions and 38376 deletions

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using Content.Server.GameTicking;
using Content.Server.Ghost;
using Content.Server.Mind;
@@ -8,6 +8,7 @@ using Content.Shared.Mind;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.Player;
namespace Content.Server.Administration.Commands;
@@ -15,7 +16,7 @@ namespace Content.Server.Administration.Commands;
public sealed class AGhostCommand : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
public override string Command => "aghost";
public override string Help => "aghost";
@@ -104,8 +105,8 @@ public sealed class AGhostCommand : LocalizedCommands
// TODO: Remove duplication between all this and "GamePreset.OnGhostAttempt()"...
if (!string.IsNullOrWhiteSpace(mind.CharacterName))
metaDataSystem.SetEntityName(ghost, mind.CharacterName);
else if (!string.IsNullOrWhiteSpace(mind.Session?.Name))
metaDataSystem.SetEntityName(ghost, mind.Session.Name);
else if (!string.IsNullOrWhiteSpace(player.Name))
metaDataSystem.SetEntityName(ghost, player.Name);
mindSystem.Visit(mindId, ghost, mind);
}
@@ -116,6 +117,6 @@ public sealed class AGhostCommand : LocalizedCommands
}
var comp = _entities.GetComponent<GhostComponent>(ghost);
ghostSystem.SetCanReturnToBody(comp, canReturn);
ghostSystem.SetCanReturnToBody((ghost, comp), canReturn);
}
}

View File

@@ -11,7 +11,6 @@ namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Admin)]
public sealed class ForceGhostCommand : LocalizedEntityCommands
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;

View File

@@ -4,6 +4,7 @@ using Content.Server.Warps;
using Content.Shared.Administration;
using Content.Shared.Follower;
using Content.Shared.Ghost;
using Content.Shared.Warps;
using Robust.Shared.Console;
using Robust.Shared.Enums;
using Robust.Shared.Map;

View File

@@ -1,37 +1,31 @@
using Content.Server.Administration.Systems;
using Content.Shared.Climbing.Components;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Administration.Components;
/// <summary>
/// Component to track the timer for the SuperBonk smite.
/// </summary>
[RegisterComponent, Access(typeof(SuperBonkSystem))]
public sealed partial class SuperBonkComponent: Component
[RegisterComponent, AutoGenerateComponentPause, Access(typeof(SuperBonkSystem))]
public sealed partial class SuperBonkComponent : Component
{
/// <summary>
/// Entity being Super Bonked.
/// </summary>
[DataField]
public EntityUid Target;
/// <summary>
/// All of the tables the target will be bonked on.
/// </summary>
[DataField]
public Dictionary<EntityUid, BonkableComponent>.Enumerator Tables;
public List<EntityUid>.Enumerator Tables;
/// <summary>
/// Value used to reset the timer once it expires.
/// How often should we bonk.
/// </summary>
[DataField]
public float InitialTime = 0.10f;
public TimeSpan BonkCooldown = TimeSpan.FromMilliseconds(100);
/// <summary>
/// Timer till the next bonk.
/// Next time when we will bonk.
/// </summary>
[DataField]
public float TimeRemaining = 0.10f;
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
public TimeSpan NextBonk = TimeSpan.Zero;
/// <summary>
/// Whether to remove the clumsy component from the target after SuperBonk is done.

View File

@@ -330,7 +330,12 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa
var cachedInfo = adminSys.GetCachedPlayerInfo(new NetUserId(id));
if (cachedInfo != null && cachedInfo.Antag)
{
logMessage += " [ANTAG: " + cachedInfo.CharacterName + "]";
var subtype = Loc.GetString(cachedInfo.Subtype ?? cachedInfo.RoleProto.Name);
logMessage = Loc.GetString(
"admin-alert-antag-label",
("message", logMessage),
("name", cachedInfo.CharacterName),
("subtype", subtype));
}
}

View File

@@ -1,4 +1,4 @@
using System.Text.Json;
using System.Text.Json;
using Content.Shared.Station.Components;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
@@ -72,6 +72,6 @@ public readonly struct SerializableEntityCoordinates
EntityUid = coordinates.EntityId;
X = coordinates.X;
Y = coordinates.Y;
MapUid = coordinates.GetMapUid(entityManager);
MapUid = entityManager.System<SharedTransformSystem>().GetMap(coordinates);
}
}

View File

@@ -4,10 +4,8 @@ using System.Reflection;
using System.Threading.Tasks;
using Content.Server.Chat.Managers;
using Content.Server.Database;
using Content.Server.Players;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Info;
using Content.Shared.Players;
using Robust.Server.Console;
using Robust.Server.Player;
@@ -108,7 +106,7 @@ namespace Content.Server.Administration.Managers
// The DB function handles this scenario fine, but it's worth noting.
await _dbManager.UpdateAdminDeadminnedAsync(player.UserId, newState);
}
catch (Exception e)
catch (Exception)
{
_sawmill.Error("Failed to save deadmin state to database for {Admin}", player.UserId);
}

View File

@@ -8,6 +8,7 @@ using Content.Server.EUI;
using Content.Shared.Administration;
using Content.Shared.Database;
using Content.Shared.Eui;
using Content.Shared.Follower;
using Robust.Server.Player;
using Robust.Shared.Player;
@@ -33,11 +34,13 @@ public sealed class PlayerPanelEui : BaseEui
private bool _frozen;
private bool _canFreeze;
private bool _canAhelp;
private FollowerSystem _follower;
public PlayerPanelEui(LocatedPlayerData player)
{
IoCManager.InjectDependencies(this);
_targetPlayer = player;
_follower = _entity.System<FollowerSystem>();
}
public override void Opened()
@@ -141,6 +144,16 @@ public sealed class PlayerPanelEui : BaseEui
_entity.DeleteEntity(session.AttachedEntity);
}
break;
case PlayerPanelFollowMessage:
if (!_admins.HasAdminFlag(Player, AdminFlags.Admin) ||
!_player.TryGetSessionById(_targetPlayer.UserId, out session) ||
session.AttachedEntity == null ||
Player.AttachedEntity is null ||
session.AttachedEntity == Player.AttachedEntity)
return;
_follower.StartFollowingEntity(Player.AttachedEntity.Value, session.AttachedEntity.Value);
break;
}
}

View File

@@ -153,9 +153,7 @@ public sealed class AdminSystem : EntitySystem
private void OnRoleEvent(RoleEvent ev)
{
var session = _minds.GetSession(ev.Mind);
if (!ev.RoleTypeUpdate || session == null)
if (!ev.RoleTypeUpdate || !_playerManager.TryGetSessionById(ev.Mind.UserId, out var session))
return;
UpdatePlayerList(session);
@@ -235,12 +233,16 @@ public sealed class AdminSystem : EntitySystem
// Starting role, antagonist status and role type
RoleTypePrototype roleType = new();
var startingRole = string.Empty;
LocId? subtype = null;
if (_minds.TryGetMind(session, out var mindId, out var mindComp) && mindComp is not null)
{
sortWeight = _role.GetRoleCompByTime(mindComp)?.Comp.SortWeight ?? 0;
if (_proto.TryIndex(mindComp.RoleType, out var role))
{
roleType = role;
subtype = mindComp.Subtype;
}
else
Log.Error($"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(roleType.Name)}' instead");
@@ -269,6 +271,7 @@ public sealed class AdminSystem : EntitySystem
startingRole,
antag,
roleType,
subtype,
sortWeight,
GetNetEntity(session?.AttachedEntity),
data.UserId,

View File

@@ -421,7 +421,7 @@ public sealed partial class AdminVerbSystem
{
var xform = Transform(args.Target);
var fixtures = Comp<FixturesComponent>(args.Target);
_transformSystem.Unanchor(args.Target); // Just in case.
_transformSystem.Unanchor(args.Target, xform); // Just in case.
_physics.SetBodyType(args.Target, BodyType.Dynamic, manager: fixtures, body: physics);
_physics.SetBodyStatus(args.Target, physics, BodyStatus.InAir);
_physics.WakeBody(args.Target, manager: fixtures, body: physics);
@@ -877,9 +877,9 @@ public sealed partial class AdminVerbSystem
var hadSlipComponent = EnsureComp(args.Target, out SlipperyComponent slipComponent);
if (!hadSlipComponent)
{
slipComponent.SuperSlippery = true;
slipComponent.ParalyzeTime = 5;
slipComponent.LaunchForwardsMultiplier = 20;
slipComponent.SlipData.SuperSlippery = true;
slipComponent.SlipData.ParalyzeTime = TimeSpan.FromSeconds(5);
slipComponent.SlipData.LaunchForwardsMultiplier = 20;
}
_slipperySystem.TrySlip(args.Target, slipComponent, args.Target, requiresContact: false);

View File

@@ -65,343 +65,340 @@ public sealed partial class AdminVerbSystem
if (!_adminManager.HasAdminFlag(player, AdminFlags.Admin))
return;
if (_adminManager.HasAdminFlag(player, AdminFlags.Admin))
if (TryComp<DoorBoltComponent>(args.Target, out var bolts))
{
if (TryComp<DoorBoltComponent>(args.Target, out var bolts))
Verb bolt = new()
{
Verb bolt = new()
{
Text = bolts.BoltsDown ? "Unbolt" : "Bolt",
Category = VerbCategory.Tricks,
Icon = bolts.BoltsDown
? new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/unbolt.png"))
: new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/bolt.png")),
Act = () =>
{
_door.SetBoltsDown((args.Target, bolts), !bolts.BoltsDown);
},
Impact = LogImpact.Medium,
Message = Loc.GetString(bolts.BoltsDown
? "admin-trick-unbolt-description"
: "admin-trick-bolt-description"),
Priority = (int) (bolts.BoltsDown ? TricksVerbPriorities.Unbolt : TricksVerbPriorities.Bolt),
};
args.Verbs.Add(bolt);
}
if (TryComp<AirlockComponent>(args.Target, out var airlockComp))
{
Verb emergencyAccess = new()
{
Text = airlockComp.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/emergency_access.png")),
Act = () =>
{
_airlockSystem.SetEmergencyAccess((args.Target, airlockComp), !airlockComp.EmergencyAccess);
},
Impact = LogImpact.Medium,
Message = Loc.GetString(airlockComp.EmergencyAccess
? "admin-trick-emergency-access-off-description"
: "admin-trick-emergency-access-on-description"),
Priority = (int) (airlockComp.EmergencyAccess ? TricksVerbPriorities.EmergencyAccessOff : TricksVerbPriorities.EmergencyAccessOn),
};
args.Verbs.Add(emergencyAccess);
}
if (HasComp<DamageableComponent>(args.Target))
{
Verb rejuvenate = new()
{
Text = "Rejuvenate",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rejuvenate.png")),
Act = () =>
{
_rejuvenate.PerformRejuvenate(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-rejuvenate-description"),
Priority = (int) TricksVerbPriorities.Rejuvenate,
};
args.Verbs.Add(rejuvenate);
}
if (!HasComp<GodmodeComponent>(args.Target))
{
Verb makeIndestructible = new()
{
Text = "Make Indestructible",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
Act = () =>
{
_sharedGodmodeSystem.EnableGodmode(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-make-indestructible-description"),
Priority = (int) TricksVerbPriorities.MakeIndestructible,
};
args.Verbs.Add(makeIndestructible);
}
else
{
Verb makeVulnerable = new()
{
Text = "Make Vulnerable",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
Act = () =>
{
_sharedGodmodeSystem.DisableGodmode(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-make-vulnerable-description"),
Priority = (int) TricksVerbPriorities.MakeVulnerable,
};
args.Verbs.Add(makeVulnerable);
}
if (TryComp<BatteryComponent>(args.Target, out var battery))
{
Verb refillBattery = new()
{
Text = "Refill Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
Act = () =>
{
_batterySystem.SetCharge(args.Target, battery.MaxCharge, battery);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-refill-battery-description"),
Priority = (int) TricksVerbPriorities.RefillBattery,
};
args.Verbs.Add(refillBattery);
Verb drainBattery = new()
{
Text = "Drain Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
Act = () =>
{
_batterySystem.SetCharge(args.Target, 0, battery);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-drain-battery-description"),
Priority = (int) TricksVerbPriorities.DrainBattery,
};
args.Verbs.Add(drainBattery);
Verb infiniteBattery = new()
{
Text = "Infinite Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/infinite_battery.png")),
Act = () =>
{
var recharger = EnsureComp<BatterySelfRechargerComponent>(args.Target);
recharger.AutoRecharge = true;
recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill.
recharger.AutoRechargePause = false; // No delay.
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-infinite-battery-object-description"),
Priority = (int) TricksVerbPriorities.InfiniteBattery,
};
args.Verbs.Add(infiniteBattery);
}
if (TryComp<AnchorableComponent>(args.Target, out var anchor))
{
Verb blockUnanchor = new()
{
Text = "Block Unanchoring",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/anchor.svg.192dpi.png")),
Act = () =>
{
RemComp(args.Target, anchor);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-block-unanchoring-description"),
Priority = (int) TricksVerbPriorities.BlockUnanchoring,
};
args.Verbs.Add(blockUnanchor);
}
if (TryComp<GasTankComponent>(args.Target, out var tank))
{
Verb refillInternalsO2 = new()
{
Text = "Refill Internals Oxygen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
Act = () =>
{
RefillGasTank(args.Target, Gas.Oxygen, tank);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-oxygen-description"),
Priority = (int) TricksVerbPriorities.RefillOxygen,
};
args.Verbs.Add(refillInternalsO2);
Verb refillInternalsN2 = new()
{
Text = "Refill Internals Nitrogen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
Act = () =>
{
RefillGasTank(args.Target, Gas.Nitrogen, tank);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-nitrogen-description"),
Priority = (int) TricksVerbPriorities.RefillNitrogen,
};
args.Verbs.Add(refillInternalsN2);
Verb refillInternalsPlasma = new()
{
Text = "Refill Internals Plasma",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
Act = () =>
{
RefillGasTank(args.Target, Gas.Plasma, tank);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-plasma-description"),
Priority = (int) TricksVerbPriorities.RefillPlasma,
};
args.Verbs.Add(refillInternalsPlasma);
}
if (HasComp<InventoryComponent>(args.Target))
{
Verb refillInternalsO2 = new()
{
Text = "Refill Internals Oxygen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
Act = () => RefillEquippedTanks(args.User, Gas.Oxygen),
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-oxygen-description"),
Priority = (int) TricksVerbPriorities.RefillOxygen,
};
args.Verbs.Add(refillInternalsO2);
Verb refillInternalsN2 = new()
{
Text = "Refill Internals Nitrogen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
Act = () =>RefillEquippedTanks(args.User, Gas.Nitrogen),
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-nitrogen-description"),
Priority = (int) TricksVerbPriorities.RefillNitrogen,
};
args.Verbs.Add(refillInternalsN2);
Verb refillInternalsPlasma = new()
{
Text = "Refill Internals Plasma",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
Act = () => RefillEquippedTanks(args.User, Gas.Plasma),
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-plasma-description"),
Priority = (int) TricksVerbPriorities.RefillPlasma,
};
args.Verbs.Add(refillInternalsPlasma);
}
Verb sendToTestArena = new()
{
Text = "Send to test arena",
Text = bolts.BoltsDown ? "Unbolt" : "Bolt",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")),
Icon = bolts.BoltsDown
? new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/unbolt.png"))
: new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/bolt.png")),
Act = () =>
{
var (mapUid, gridUid) = _adminTestArenaSystem.AssertArenaLoaded(player);
_transformSystem.SetCoordinates(args.Target, new EntityCoordinates(gridUid ?? mapUid, Vector2.One));
_door.SetBoltsDown((args.Target, bolts), !bolts.BoltsDown);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-send-to-test-arena-description"),
Priority = (int) TricksVerbPriorities.SendToTestArena,
Message = Loc.GetString(bolts.BoltsDown
? "admin-trick-unbolt-description"
: "admin-trick-bolt-description"),
Priority = (int)(bolts.BoltsDown ? TricksVerbPriorities.Unbolt : TricksVerbPriorities.Bolt),
};
args.Verbs.Add(sendToTestArena);
args.Verbs.Add(bolt);
}
var activeId = FindActiveId(args.Target);
if (activeId is not null)
if (TryComp<AirlockComponent>(args.Target, out var airlockComp))
{
Verb emergencyAccess = new()
{
Verb grantAllAccess = new()
Text = airlockComp.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/emergency_access.png")),
Act = () =>
{
Text = "Grant All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
Act = () =>
{
GiveAllAccess(activeId.Value);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-grant-all-access-description"),
Priority = (int) TricksVerbPriorities.GrantAllAccess,
};
args.Verbs.Add(grantAllAccess);
_airlockSystem.SetEmergencyAccess((args.Target, airlockComp), !airlockComp.EmergencyAccess);
},
Impact = LogImpact.Medium,
Message = Loc.GetString(airlockComp.EmergencyAccess
? "admin-trick-emergency-access-off-description"
: "admin-trick-emergency-access-on-description"),
Priority = (int)(airlockComp.EmergencyAccess ? TricksVerbPriorities.EmergencyAccessOff : TricksVerbPriorities.EmergencyAccessOn),
};
args.Verbs.Add(emergencyAccess);
}
Verb revokeAllAccess = new()
{
Text = "Revoke All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "default"),
Act = () =>
{
RevokeAllAccess(activeId.Value);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-revoke-all-access-description"),
Priority = (int) TricksVerbPriorities.RevokeAllAccess,
};
args.Verbs.Add(revokeAllAccess);
}
if (HasComp<AccessComponent>(args.Target))
if (HasComp<DamageableComponent>(args.Target))
{
Verb rejuvenate = new()
{
Verb grantAllAccess = new()
Text = "Rejuvenate",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rejuvenate.png")),
Act = () =>
{
Text = "Grant All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
Act = () =>
{
GiveAllAccess(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-grant-all-access-description"),
Priority = (int) TricksVerbPriorities.GrantAllAccess,
};
args.Verbs.Add(grantAllAccess);
_rejuvenate.PerformRejuvenate(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-rejuvenate-description"),
Priority = (int)TricksVerbPriorities.Rejuvenate,
};
args.Verbs.Add(rejuvenate);
}
Verb revokeAllAccess = new()
if (!HasComp<GodmodeComponent>(args.Target))
{
Verb makeIndestructible = new()
{
Text = "Make Indestructible",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
Act = () =>
{
Text = "Revoke All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "default"),
Act = () =>
{
RevokeAllAccess(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-revoke-all-access-description"),
Priority = (int) TricksVerbPriorities.RevokeAllAccess,
};
args.Verbs.Add(revokeAllAccess);
}
_sharedGodmodeSystem.EnableGodmode(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-make-indestructible-description"),
Priority = (int)TricksVerbPriorities.MakeIndestructible,
};
args.Verbs.Add(makeIndestructible);
}
else
{
Verb makeVulnerable = new()
{
Text = "Make Vulnerable",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
Act = () =>
{
_sharedGodmodeSystem.DisableGodmode(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-make-vulnerable-description"),
Priority = (int)TricksVerbPriorities.MakeVulnerable,
};
args.Verbs.Add(makeVulnerable);
}
if (TryComp<BatteryComponent>(args.Target, out var battery))
{
Verb refillBattery = new()
{
Text = "Refill Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
Act = () =>
{
_batterySystem.SetCharge(args.Target, battery.MaxCharge, battery);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-refill-battery-description"),
Priority = (int)TricksVerbPriorities.RefillBattery,
};
args.Verbs.Add(refillBattery);
Verb drainBattery = new()
{
Text = "Drain Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
Act = () =>
{
_batterySystem.SetCharge(args.Target, 0, battery);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-drain-battery-description"),
Priority = (int)TricksVerbPriorities.DrainBattery,
};
args.Verbs.Add(drainBattery);
Verb infiniteBattery = new()
{
Text = "Infinite Battery",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/infinite_battery.png")),
Act = () =>
{
var recharger = EnsureComp<BatterySelfRechargerComponent>(args.Target);
recharger.AutoRecharge = true;
recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill.
recharger.AutoRechargePause = false; // No delay.
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-infinite-battery-object-description"),
Priority = (int)TricksVerbPriorities.InfiniteBattery,
};
args.Verbs.Add(infiniteBattery);
}
if (TryComp<AnchorableComponent>(args.Target, out var anchor))
{
Verb blockUnanchor = new()
{
Text = "Block Unanchoring",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/anchor.svg.192dpi.png")),
Act = () =>
{
RemComp(args.Target, anchor);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-block-unanchoring-description"),
Priority = (int)TricksVerbPriorities.BlockUnanchoring,
};
args.Verbs.Add(blockUnanchor);
}
if (TryComp<GasTankComponent>(args.Target, out var tank))
{
Verb refillInternalsO2 = new()
{
Text = "Refill Internals Oxygen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
Act = () =>
{
RefillGasTank(args.Target, Gas.Oxygen, tank);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-oxygen-description"),
Priority = (int)TricksVerbPriorities.RefillOxygen,
};
args.Verbs.Add(refillInternalsO2);
Verb refillInternalsN2 = new()
{
Text = "Refill Internals Nitrogen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
Act = () =>
{
RefillGasTank(args.Target, Gas.Nitrogen, tank);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-nitrogen-description"),
Priority = (int)TricksVerbPriorities.RefillNitrogen,
};
args.Verbs.Add(refillInternalsN2);
Verb refillInternalsPlasma = new()
{
Text = "Refill Internals Plasma",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
Act = () =>
{
RefillGasTank(args.Target, Gas.Plasma, tank);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-plasma-description"),
Priority = (int)TricksVerbPriorities.RefillPlasma,
};
args.Verbs.Add(refillInternalsPlasma);
}
if (HasComp<InventoryComponent>(args.Target))
{
Verb refillInternalsO2 = new()
{
Text = "Refill Internals Oxygen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
Act = () => RefillEquippedTanks(args.User, Gas.Oxygen),
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-oxygen-description"),
Priority = (int)TricksVerbPriorities.RefillOxygen,
};
args.Verbs.Add(refillInternalsO2);
Verb refillInternalsN2 = new()
{
Text = "Refill Internals Nitrogen",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
Act = () => RefillEquippedTanks(args.User, Gas.Nitrogen),
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-nitrogen-description"),
Priority = (int)TricksVerbPriorities.RefillNitrogen,
};
args.Verbs.Add(refillInternalsN2);
Verb refillInternalsPlasma = new()
{
Text = "Refill Internals Plasma",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
Act = () => RefillEquippedTanks(args.User, Gas.Plasma),
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-plasma-description"),
Priority = (int)TricksVerbPriorities.RefillPlasma,
};
args.Verbs.Add(refillInternalsPlasma);
}
Verb sendToTestArena = new()
{
Text = "Send to test arena",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")),
Act = () =>
{
var (mapUid, gridUid) = _adminTestArenaSystem.AssertArenaLoaded(player);
_transformSystem.SetCoordinates(args.Target, new EntityCoordinates(gridUid ?? mapUid, Vector2.One));
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-send-to-test-arena-description"),
Priority = (int)TricksVerbPriorities.SendToTestArena,
};
args.Verbs.Add(sendToTestArena);
var activeId = FindActiveId(args.Target);
if (activeId is not null)
{
Verb grantAllAccess = new()
{
Text = "Grant All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
Act = () =>
{
GiveAllAccess(activeId.Value);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-grant-all-access-description"),
Priority = (int)TricksVerbPriorities.GrantAllAccess,
};
args.Verbs.Add(grantAllAccess);
Verb revokeAllAccess = new()
{
Text = "Revoke All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "default"),
Act = () =>
{
RevokeAllAccess(activeId.Value);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-revoke-all-access-description"),
Priority = (int)TricksVerbPriorities.RevokeAllAccess,
};
args.Verbs.Add(revokeAllAccess);
}
if (HasComp<AccessComponent>(args.Target))
{
Verb grantAllAccess = new()
{
Text = "Grant All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
Act = () =>
{
GiveAllAccess(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-grant-all-access-description"),
Priority = (int)TricksVerbPriorities.GrantAllAccess,
};
args.Verbs.Add(grantAllAccess);
Verb revokeAllAccess = new()
{
Text = "Revoke All Access",
Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "default"),
Act = () =>
{
RevokeAllAccess(args.Target);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-revoke-all-access-description"),
Priority = (int)TricksVerbPriorities.RevokeAllAccess,
};
args.Verbs.Add(revokeAllAccess);
}
if (TryComp<StackComponent>(args.Target, out var stack))

View File

@@ -2,16 +2,12 @@ using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers;
using Content.Server.Administration.UI;
using Content.Server.Disposal.Tube;
using Content.Server.Disposal.Tube.Components;
using Content.Server.EUI;
using Content.Server.GameTicking;
using Content.Server.Ghost.Roles;
using Content.Server.Mind;
using Content.Server.Mind.Commands;
using Content.Server.Prayer;
using Content.Server.Station.Systems;
using Content.Server.Xenoarchaeology.XenoArtifacts;
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
using Content.Shared.Administration;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
@@ -26,7 +22,6 @@ using Content.Shared.Verbs;
using Robust.Server.Console;
using Robust.Server.GameObjects;
using Robust.Shared.Console;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
@@ -58,9 +53,7 @@ namespace Content.Server.Administration.Systems
[Dependency] private readonly AdminSystem _adminSystem = default!;
[Dependency] private readonly DisposalTubeSystem _disposalTubes = default!;
[Dependency] private readonly EuiManager _euiManager = default!;
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly GhostRoleSystem _ghostRoleSystem = default!;
[Dependency] private readonly ArtifactSystem _artifactSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly PrayerSystem _prayerSystem = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
@@ -151,7 +144,7 @@ namespace Content.Server.Administration.Systems
var stationUid = _stations.GetOwningStation(args.Target);
var profile = _ticker.GetPlayerProfile(targetActor.PlayerSession);
var profile = _gameTicker.GetPlayerProfile(targetActor.PlayerSession);
var mobUid = _spawning.SpawnPlayerMob(coords.Value, null, profile, stationUid);
if (_mindSystem.TryGetMind(args.Target, out var mindId, out var mindComp))
@@ -177,7 +170,7 @@ namespace Content.Server.Administration.Systems
var stationUid = _stations.GetOwningStation(args.Target);
var profile = _ticker.GetPlayerProfile(targetActor.PlayerSession);
var profile = _gameTicker.GetPlayerProfile(targetActor.PlayerSession);
_spawning.SpawnPlayerMob(coords.Value, null, profile, stationUid);
},
ConfirmationPopup = true,
@@ -194,7 +187,7 @@ namespace Content.Server.Administration.Systems
});
}
if (_mindSystem.TryGetMind(args.Target, out _, out var mind) && mind.UserId != null)
if (_mindSystem.TryGetMind(args.Target, out var mindId, out var mindComp) && mindComp.UserId != null)
{
// Erase
args.Verbs.Add(new Verb
@@ -206,7 +199,7 @@ namespace Content.Server.Administration.Systems
new("/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png")),
Act = () =>
{
_adminSystem.Erase(mind.UserId.Value);
_adminSystem.Erase(mindComp.UserId.Value);
},
Impact = LogImpact.Extreme,
ConfirmationPopup = true
@@ -219,11 +212,20 @@ namespace Content.Server.Administration.Systems
Category = VerbCategory.Admin,
Act = () =>
{
_console.ExecuteCommand(player, $"respawn \"{mind.UserId}\"");
_console.ExecuteCommand(player, $"respawn \"{mindComp.UserId}\"");
},
ConfirmationPopup = true,
// No logimpact as the command does it internally.
});
// Inspect mind
args.Verbs.Add(new Verb
{
Text = Loc.GetString("inspect-mind-verb-get-data-text"),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/sentient.svg.192dpi.png")),
Category = VerbCategory.Debug,
Act = () => _console.RemoteExecuteCommand(player, $"vv {GetNetEntity(mindId)}"),
});
}
// Freeze
@@ -445,29 +447,6 @@ namespace Content.Server.Administration.Systems
args.Verbs.Add(verb);
}
// XenoArcheology
if (_adminManager.IsAdmin(player) && TryComp<ArtifactComponent>(args.Target, out var artifact))
{
// make artifact always active (by adding timer trigger)
args.Verbs.Add(new Verb()
{
Text = Loc.GetString("artifact-verb-make-always-active"),
Category = VerbCategory.Debug,
Act = () => EntityManager.AddComponent<ArtifactTimerTriggerComponent>(args.Target),
Disabled = EntityManager.HasComponent<ArtifactTimerTriggerComponent>(args.Target),
Impact = LogImpact.High
});
// force to activate artifact ignoring timeout
args.Verbs.Add(new Verb()
{
Text = Loc.GetString("artifact-verb-activate"),
Category = VerbCategory.Debug,
Act = () => _artifactSystem.ForceActivateArtifact(args.Target, component: artifact),
Impact = LogImpact.High
});
}
// Make Sentient verb
if (_groupController.CanCommand(player, "makesentient") &&
args.User != args.Target &&

View File

@@ -4,6 +4,7 @@ using Content.Shared.Clumsy;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Timing;
namespace Content.Server.Administration.Systems;
@@ -12,44 +13,38 @@ public sealed class SuperBonkSystem : EntitySystem
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly ClumsySystem _clumsySystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly IGameTiming _timing = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SuperBonkComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<SuperBonkComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<SuperBonkComponent, ComponentShutdown>(OnBonkShutdown);
SubscribeLocalEvent<SuperBonkComponent, ComponentShutdown>(OnShutdown);
}
public void StartSuperBonk(EntityUid target, float delay = 0.1f, bool stopWhenDead = false)
private void OnInit(Entity<SuperBonkComponent> ent, ref ComponentInit args)
{
var (_, component) = ent;
//The other check in the code to stop when the target dies does not work if the target is already dead.
if (stopWhenDead && TryComp<MobStateComponent>(target, out var mState))
{
if (mState.CurrentState == MobState.Dead)
return;
}
component.NextBonk = _timing.CurTime + component.BonkCooldown;
}
var hadClumsy = EnsureComp<ClumsyComponent>(target, out _);
private void OnMobStateChanged(Entity<SuperBonkComponent> ent, ref MobStateChangedEvent args)
{
var (uid, component) = ent;
var tables = EntityQueryEnumerator<BonkableComponent>();
var bonks = new Dictionary<EntityUid, BonkableComponent>();
// This is done so we don't crash if something like a new table is spawned.
while (tables.MoveNext(out var uid, out var comp))
{
bonks.Add(uid, comp);
}
if (component.StopWhenDead && args.NewMobState == MobState.Dead)
RemCompDeferred<SuperBonkComponent>(uid);
}
var sComp = new SuperBonkComponent
{
Target = target,
Tables = bonks.GetEnumerator(),
RemoveClumsy = !hadClumsy,
StopWhenDead = stopWhenDead,
};
private void OnShutdown(Entity<SuperBonkComponent> ent, ref ComponentShutdown args)
{
var (uid, component) = ent;
AddComp(target, sComp);
if (component.RemoveClumsy)
RemComp<ClumsyComponent>(uid);
}
public override void Update(float frameTime)
@@ -59,49 +54,58 @@ public sealed class SuperBonkSystem : EntitySystem
while (comps.MoveNext(out var uid, out var comp))
{
comp.TimeRemaining -= frameTime;
if (!(comp.TimeRemaining <= 0))
if (comp.NextBonk > _timing.CurTime)
continue;
Bonk(comp);
if (!(comp.Tables.MoveNext()))
if (!TryBonk(uid, comp.Tables.Current) || !comp.Tables.MoveNext())
{
RemComp<SuperBonkComponent>(comp.Target);
RemComp<SuperBonkComponent>(uid);
continue;
}
comp.TimeRemaining = comp.InitialTime;
comp.NextBonk += comp.BonkCooldown;
}
}
private void Bonk(SuperBonkComponent comp)
public void StartSuperBonk(EntityUid target, bool stopWhenDead = false)
{
var uid = comp.Tables.Current.Key;
//The other check in the code to stop when the target dies does not work if the target is already dead.
if (stopWhenDead && TryComp<MobStateComponent>(target, out var mobState) && mobState.CurrentState == MobState.Dead)
return;
if (EnsureComp<SuperBonkComponent>(target, out var component))
return;
var tables = EntityQueryEnumerator<BonkableComponent>();
var bonks = new List<EntityUid>();
// This is done so we don't crash if something like a new table is spawned.
while (tables.MoveNext(out var uid, out var comp))
{
bonks.Add(uid);
}
component.Tables = bonks.GetEnumerator();
component.RemoveClumsy = !EnsureComp<ClumsyComponent>(target, out _);
component.StopWhenDead = stopWhenDead;
}
private bool TryBonk(EntityUid uid, EntityUid tableUid)
{
if (!TryComp<ClumsyComponent>(uid, out var clumsyComp))
return false;
// It would be very weird for something without a transform component to have a bonk component
// but just in case because I don't want to crash the server.
if (!HasComp<TransformComponent>(uid) || !TryComp<ClumsyComponent>(comp.Target, out var clumsyComp))
return;
_transformSystem.SetCoordinates(comp.Target, Transform(uid).Coordinates);
_clumsySystem.HitHeadClumsy((comp.Target, clumsyComp), uid);
_audioSystem.PlayPvs(clumsyComp.TableBonkSound, comp.Target);
}
private void OnMobStateChanged(EntityUid uid, SuperBonkComponent comp, MobStateChangedEvent args)
{
if (comp.StopWhenDead && args.NewMobState == MobState.Dead)
if (HasComp<TransformComponent>(tableUid))
{
RemComp<SuperBonkComponent>(uid);
}
}
_transformSystem.SetCoordinates(uid, Transform(tableUid).Coordinates);
private void OnBonkShutdown(EntityUid uid, SuperBonkComponent comp, ComponentShutdown ev)
{
if (comp.RemoveClumsy)
RemComp<ClumsyComponent>(comp.Target);
_clumsySystem.HitHeadClumsy((uid, clumsyComp), tableUid);
_audioSystem.PlayPvs(clumsyComp.TableBonkSound, tableUid);
}
return true;
}
}

View File

@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using Content.Shared.Administration;
using Content.Shared.Tag;
using Robust.Shared.Prototypes;
@@ -29,10 +29,10 @@ public sealed class TagCommand : ToolshedCommand
public IEnumerable<EntityUid> With(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] IEnumerable<EntityUid> entities,
[CommandArgument] ValueRef<string, Prototype<TagPrototype>> tag)
[CommandArgument] ProtoId<TagPrototype> tag)
{
_tag ??= GetSys<TagSystem>();
return entities.Where(e => _tag.HasTag(e, tag.Evaluate(ctx)!));
return entities.Where(e => _tag.HasTag(e, tag!));
}
[CommandImplementation("add")]