Skip to content

Commit ab4e12d

Browse files
authored
Merge branch 'DeltaV-Station:master' into Pebble-Faxes
2 parents 48112fa + 1986fbf commit ab4e12d

87 files changed

Lines changed: 1041 additions & 277 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Content.Client/_DV/Body/FeatherVisualizer.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,10 @@ public sealed class FeatherVisualizer : VisualizerSystem<FeatherComponent>
1414

1515
protected override void OnAppearanceChange(EntityUid uid, FeatherComponent component, ref AppearanceChangeEvent args)
1616
{
17-
if (!AppearanceSystem.TryGetData<Color>(uid, FeatherVisuals.BloodColor, out var bloodColor, args.Component) ||
18-
!AppearanceSystem.TryGetData<Color>(uid, FeatherVisuals.FeatherColor, out var featherColor, args.Component))
19-
{
17+
if (!AppearanceSystem.TryGetData<Color>(uid, FeatherVisuals.FeatherColor, out var featherColor, args.Component))
2018
return;
21-
}
2219

2320
SpriteSystem.LayerSetColor(uid, FeatherVisualLayers.Feather, featherColor);
24-
SpriteSystem.LayerSetColor(uid, FeatherVisualLayers.Blood, bloodColor);
2521

2622
if (TryComp<ClothingComponent>(uid, out var clothing))
2723
{
@@ -30,6 +26,11 @@ protected override void OnAppearanceChange(EntityUid uid, FeatherComponent compo
3026
_clothing.SetLayerColor(clothing, slotPair.Key, "feather", featherColor);
3127
}
3228
}
29+
30+
if (!AppearanceSystem.TryGetData<Color>(uid, FeatherVisuals.BloodColor, out var bloodColor, args.Component))
31+
return;
32+
33+
SpriteSystem.LayerSetColor(uid, FeatherVisualLayers.Blood, bloodColor);
3334
}
3435
}
3536

Content.Server/Chat/Managers/ChatManager.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Content.Shared.Chat;
1313
using Content.Shared.Database;
1414
using Content.Shared.Mind;
15+
using Content.Shared.Players; // DeltaV - OOC muting
1516
using Content.Shared.Players.RateLimiting;
1617
using Robust.Shared.Configuration;
1718
using Robust.Shared.Network;
@@ -283,6 +284,11 @@ private void SendOOC(ICommonSession player, string message)
283284
return;
284285
}
285286

287+
// DeltaV - OOC muting START
288+
if (player.ContentData() is { OocMuted: true })
289+
return;
290+
// DeltaV END
291+
286292
Color? colorOverride = null;
287293
var wrappedMessage = Loc.GetString("chat-manager-send-ooc-wrap-message", ("playerName",player.Name), ("message", FormattedMessage.EscapeText(message)));
288294
if (_adminManager.HasAdminFlag(player, AdminFlags.Admin))
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using Content.Server._DV.Administration.Components;
2+
using Content.Server.Administration;
3+
using Content.Server.Administration.Logs;
4+
using Content.Server.Chat.Managers;
5+
using Content.Shared.Administration;
6+
using Content.Shared.Database;
7+
using Content.Shared.Players;
8+
using Robust.Server.Player;
9+
using Robust.Shared.Console;
10+
11+
namespace Content.Server._DV.Administration.Commands;
12+
13+
/// <summary>
14+
/// Command for muting/unmuting specific players in specific OOC channels.
15+
/// Mutes are not persisted to storage.
16+
/// </summary>
17+
///
18+
/// After adding/removing a channel in <see cref="Execute"/>, don't forget to:
19+
/// - Update <see cref="CHANNEL_COMPLETION_OPTIONS"/>
20+
/// - Document behavior in "cmd-mute-help" in Resources/Locale/en-US/_DV/administration/commands/mute.ftl
21+
22+
[AdminCommand(AdminFlags.Ban)]
23+
public sealed class MuteCommand : LocalizedEntityCommands
24+
{
25+
[Dependency] private readonly IPlayerManager _player = default!;
26+
[Dependency] private readonly IChatManager _chat = default!;
27+
[Dependency] private readonly IAdminLogManager _adminLogs = default!;
28+
29+
private static readonly string[] ChannelCompletionOptions = ["OOC", "LOOC", "DEADCHAT"];
30+
private static readonly string ChannelListText = string.Join(", ", ChannelCompletionOptions);
31+
32+
public override string Command => "mute";
33+
public override string Help => Loc.GetString("cmd-mute-help", ("channels", ChannelListText), ("command", Command));
34+
35+
public override void Execute(IConsoleShell shell, string argStr, string[] args)
36+
{
37+
if (args.Length != 2)
38+
{
39+
shell.WriteLine(Help);
40+
return;
41+
}
42+
43+
if (!_player.TryGetSessionByUsername(args[1], out var session))
44+
{
45+
shell.WriteLine(Loc.GetString("shell-target-player-does-not-exist"));
46+
return;
47+
}
48+
49+
bool isMuted;
50+
switch (args[0].ToLower())
51+
{
52+
case "deadchat":
53+
{
54+
if (session.GetMind() is not { } mind)
55+
{
56+
shell.WriteLine(Loc.GetString("cmd-mute-err-missing-mind"));
57+
return;
58+
}
59+
60+
var mute = EntityManager.EnsureComponent<InGameOocMutedComponent>(mind);
61+
mute.MuteDeadchat = !mute.MuteDeadchat;
62+
isMuted = mute.MuteDeadchat;
63+
}
64+
break;
65+
66+
case "looc":
67+
{
68+
if (session.GetMind() is not { } mind)
69+
{
70+
shell.WriteLine(Loc.GetString("cmd-mute-err-missing-mind"));
71+
return;
72+
}
73+
74+
var mute = EntityManager.EnsureComponent<InGameOocMutedComponent>(mind);
75+
mute.MuteLooc = !mute.MuteLooc;
76+
isMuted = mute.MuteLooc;
77+
}
78+
break;
79+
80+
case "ooc":
81+
if (session.ContentData() is not { } playerData)
82+
{
83+
shell.WriteLine(Loc.GetString("cmd-mute-err-no-data"));
84+
return;
85+
}
86+
87+
playerData.OocMuted = !playerData.OocMuted;
88+
isMuted = playerData.OocMuted;
89+
break;
90+
91+
default:
92+
shell.WriteLine(Loc.GetString("cmd-mute-err-unknown-channel", ("channels", ChannelListText)));
93+
return;
94+
}
95+
96+
var channelName = args[0].ToUpper();
97+
_adminLogs.Add(LogType.AdminCommands,
98+
LogImpact.Extreme,
99+
$"{session:player} has been {(isMuted ? "muted" : "unmuted")} in {channelName} by {shell.Player?.Name ?? "<UNKNOWN>"}");
100+
101+
var locArgs = ("chat", channelName);
102+
_chat.DispatchServerMessage(session,
103+
isMuted
104+
? Loc.GetString("cmd-mute-player-notif-muted", locArgs)
105+
: Loc.GetString("cmd-mute-player-notif-unmuted", locArgs),
106+
true);
107+
108+
shell.WriteLine(Loc.GetString("shell-command-success"));
109+
}
110+
111+
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
112+
{
113+
switch (args.Length)
114+
{
115+
case 1:
116+
return CompletionResult.FromHintOptions(ChannelCompletionOptions, "channel");
117+
case 2:
118+
return CompletionResult.FromOptions(CompletionHelper.SessionNames());
119+
default:
120+
return CompletionResult.Empty;
121+
}
122+
}
123+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Content.Server._DV.Administration.Components;
2+
3+
/// <summary>
4+
/// This Component is attached to mind entities of players whose LOOC/Deadchat access should be restricted.
5+
/// </summary>
6+
[RegisterComponent]
7+
public sealed partial class InGameOocMutedComponent : Component
8+
{
9+
public bool MuteLooc;
10+
public bool MuteDeadchat;
11+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Content.Server._DV.Administration.Components;
2+
using Content.Server.GameTicking;
3+
using Content.Shared.Chat;
4+
using Content.Shared.Players;
5+
6+
namespace Content.Server._DV.Administration.EntitySystems;
7+
8+
public sealed class InGameOocMutedSystem : EntitySystem
9+
{
10+
public override void Initialize()
11+
{
12+
base.Initialize();
13+
14+
SubscribeLocalEvent<InGameOocMessageAttemptEvent>(OnIngameOoc);
15+
}
16+
17+
private void OnIngameOoc(ref InGameOocMessageAttemptEvent args)
18+
{
19+
if (args.Session.GetMind() is not { } mind)
20+
return;
21+
22+
if (!TryComp<InGameOocMutedComponent>(mind, out var oocMuted))
23+
return;
24+
25+
if (oocMuted.MuteDeadchat && args.Type == InGameOOCChatType.Dead
26+
|| oocMuted.MuteLooc && args.Type == InGameOOCChatType.Looc)
27+
{
28+
args.Cancelled = true;
29+
}
30+
}
31+
}

Content.Server/_DV/Psionics/Systems/PsionicPowers/MassSleepPowerSystem.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,22 @@
33
using Content.Shared._DV.Psionics.Events.PowerActionEvents;
44
using Content.Shared._DV.Psionics.Events.PowerDoAfterEvents;
55
using Content.Shared._DV.Psionics.Systems.PsionicPowers;
6-
using Content.Shared.Bed.Sleep;
76
using Content.Shared.DoAfter;
87
using Content.Shared.Movement.Systems;
98
using Content.Shared.Popups;
9+
using Content.Shared.StatusEffectNew;
1010
using Robust.Shared.Prototypes;
1111

1212
namespace Content.Server._DV.Psionics.Systems.PsionicPowers;
1313

1414
public sealed class MassSleepPowerSystem : SharedMassSleepPowerSystem
1515
{
1616
[Dependency] private readonly EntityLookupSystem _lookup = default!;
17-
[Dependency] private readonly Shared.StatusEffectNew.StatusEffectsSystem _statusEffects = default!;
17+
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
1818
[Dependency] private readonly MovementModStatusSystem _movementMod = default!;
1919

2020
public static readonly EntProtoId MassSleepSlowdown = "MassSleepSlowdownStatusEffect";
21+
public static readonly EntProtoId MassSleepStatusEffect = "MassSleepForcedSleepStatusEffect";
2122
private EntityQuery<PsionicPowerDetectorComponent> _psionicDetectorQuery;
2223

2324
public override void Initialize()
@@ -75,8 +76,10 @@ private void OnMassSleepDoAfter(Entity<MassSleepPowerComponent> psionic, ref Mas
7576

7677
foreach (var target in _lookup.GetEntitiesInRange(args.User, psionic.Comp.Radius))
7778
{
78-
if (args.Used != target && Psionic.CanBeTargeted(target))
79-
_statusEffects.TryUpdateStatusEffectDuration(target, SleepingSystem.StatusEffectForcedSleeping, psionic.Comp.Duration);
79+
if (args.Used == target || !Psionic.CanBeTargeted(target))
80+
continue;
81+
82+
_statusEffects.TryUpdateStatusEffectDuration(target, MassSleepStatusEffect, psionic.Comp.Duration);
8083
}
8184
}
8285
}
Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,74 @@
11
using Content.Server.Electrocution;
22
using Content.Server.Lightning;
3+
using Content.Server.Power.EntitySystems;
34
using Content.Shared._DV.Psionics.Components.PsionicPowers;
5+
using Content.Shared._DV.Psionics.Events;
46
using Content.Shared._DV.Psionics.Events.PowerActionEvents;
57
using Content.Shared._DV.Psionics.Systems.PsionicPowers;
8+
using Content.Shared.IdentityManagement;
9+
using Content.Shared.Popups;
10+
using Content.Shared.Power.Components;
11+
using Content.Shared.PowerCell;
12+
using Content.Shared.PowerCell.Components;
613

714
namespace Content.Server._DV.Psionics.Systems.PsionicPowers;
815

916
public sealed class NoosphericZapPowerSystem : SharedNoosphericZapPowerSystem
1017
{
11-
[Dependency] private readonly LightningSystem _lightning = default!;
18+
[Dependency] private readonly BatterySystem _battery = default!;
1219
[Dependency] private readonly ElectrocutionSystem _electrocution = default!;
20+
[Dependency] private readonly LightningSystem _lightning = default!;
21+
[Dependency] private readonly PowerCellSystem _powerCell = default!;
22+
23+
public override void Initialize()
24+
{
25+
base.Initialize();
26+
27+
SubscribeLocalEvent<PowerCellSlotComponent, NoosphericallyZappedEvent>(OnBatterySlotZapped);
28+
SubscribeLocalEvent<BatteryComponent, NoosphericallyZappedEvent>(OnBatteryZapped);
29+
}
1330

1431
protected override void OnPowerUsed(Entity<NoosphericZapPowerComponent> psionic, ref NoosphericZapPowerActionEvent args)
1532
{
16-
if (!Psionic.CanBeTargeted(args.Target, hasAggressor: args.Performer))
33+
// As this can target batteries, it doesn't require the target to be psionic.
34+
if (!Psionic.CanBeTargeted(args.Target, ignorePsionicRequirement: true, hasAggressor: args.Performer))
1735
return;
1836

37+
var ev = new NoosphericallyZappedEvent(psionic.Comp.AddedBatteryCharge, args.Performer);
38+
RaiseLocalEvent(args.Target, ref ev);
39+
40+
// If they don't have anything else, we check if they're a potential psionic.
41+
if (!ev.CanZap && !Psionic.CanBeTargeted(args.Target, hasAggressor: args.Performer))
42+
return;
43+
44+
var message = Loc.GetString("psionic-power-noospheric-zap-user", ("user", Identity.Entity(args.Performer, EntityManager)));
45+
Popup.PopupEntity(message, args.Performer, PopupType.LargeCaution);
46+
1947
_lightning.ShootLightning(args.Performer, args.Target, psionic.Comp.LightningPrototpyeId);
2048
_electrocution.TryDoElectrocution(args.Target, args.Performer, psionic.Comp.ShockDamage, psionic.Comp.StunDuration, true);
2149

2250
AfterPowerUsed(psionic, args.Performer);
2351
}
52+
53+
private void OnBatterySlotZapped(Entity<PowerCellSlotComponent> batterySlot, ref NoosphericallyZappedEvent args)
54+
{
55+
if (!_powerCell.TryGetBatteryFromEntityOrSlot(batterySlot.Owner, out var battery))
56+
return;
57+
58+
ChargeBattery(battery.Value.AsNullable(), args.RechargeAmount, batterySlot);
59+
args.CanZap = true;
60+
}
61+
62+
private void OnBatteryZapped(Entity<BatteryComponent> battery, ref NoosphericallyZappedEvent args)
63+
{
64+
ChargeBattery(battery.AsNullable(), args.RechargeAmount, battery);
65+
args.CanZap = true;
66+
}
67+
68+
private void ChargeBattery(Entity<BatteryComponent?> battery, float amount, EntityUid container)
69+
{
70+
var message = Loc.GetString("psionic-power-noospheric-zap-battery", ("battery", Identity.Entity(container, EntityManager)));
71+
Popup.PopupEntity(message, battery, PopupType.Medium);
72+
_battery.ChangeCharge(battery, amount);
73+
}
2474
}

Content.Shared/PDA/SharedRingerSystem.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ public override void Update(float frameTime)
5555
if (curTime < ringer.NextNoteTime.Value)
5656
continue;
5757

58+
// DeltaV Start - Silicons: Send PDA ringer sound to the player directly
59+
// Check if this ringer is also a player. If so, the sound needs to play to the player itself.
60+
if (HasComp<ActorComponent>(uid))
61+
{
62+
if (_net.IsServer)
63+
{
64+
_audio.PlayGlobal(
65+
GetSound(ringer.Ringtone[ringer.NoteCount]),
66+
Filter.Entities(uid),
67+
true,
68+
AudioParams.Default.WithVolume(ringer.Volume)
69+
);
70+
}
71+
}
72+
else
73+
// DeltaV End - Silicons: Send PDA ringer sound to the player directly
5874
// Play the note
5975
// We only do this on the server because otherwise the sound either dupes or blends into a mess
6076
// There's no easy way to figure out which player started it, so that we can exclude them from the list

Content.Shared/Players/ContentPlayerData.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public sealed class ContentPlayerData
4343
[ViewVariables]
4444
public bool Whitelisted { get; set; }
4545

46+
// DeltaV - OOC muting START
47+
[ViewVariables]
48+
public bool OocMuted { get; set; }
49+
// DeltaV END
50+
4651
public ContentPlayerData(NetUserId userId, string name)
4752
{
4853
UserId = userId;

Content.Shared/_DV/Body/Systems/PreenableSystem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ private void OnCompInit(Entity<PreenableComponent> ent, ref ComponentInit args)
5454

5555
private void AddVerb(Entity<PreenableComponent> ent, ref GetVerbsEvent<Verb> args)
5656
{
57-
if (!args.CanInteract)
57+
if (!args.CanInteract || !args.CanAccess)
5858
return;
5959

6060
// can't preen with no feathers

0 commit comments

Comments
 (0)