Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут кролик накидал очень много полезных комментариев.

Из важного:
Перенести проверку боевоего режима в shared, а тут оставить всё остальное.

При отказе в доступе нету попапа или какой либо причины в отказе.

Ну и ещё исправления.

Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
using Content.Shared._Arcane.AggressionInhibitor.Components;
using Robust.Shared.Containers;
using Content.Shared.CombatMode;
using Content.Shared.Popups;
using Content.Shared.Interaction;
using Content.Shared.Access.Components;
using Content.Server.Access.Systems;
using Robust.Shared.Audio.Systems;
using Content.Server.Administration;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Inventory;
using Robust.Server.Player;
using Robust.Shared.Timing;

namespace Content.Server._Arcane.AggressionInhibitor.Systems;

public sealed partial class AggressionInhibitorSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private InventorySystem _inventorySystem = default!;
[Dependency] private SharedTransformSystem _transformSystem = default!;
[Dependency] private SharedPopupSystem _popup = default!;
[Dependency] private IdCardSystem _idCard = default!;
[Dependency] private SharedAudioSystem _audio = default!;
[Dependency] private SharedHandsSystem _handsSystem = default!;
[Dependency] private QuickDialogSystem _quickDialog = default!;
[Dependency] private SharedCombatModeSystem _combatMode = default!;
[Dependency] private SharedContainerSystem _containerSystem = default!;
[Dependency] private IPlayerManager _playerManager = default!;
Comment on lines +17 to +29

This comment was marked as resolved.


public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<AggressionInhibitorComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<AggressionInhibitorComponent, OpenDialogEvent>(OnOpenDialogReceived);
SubscribeLocalEvent<AggressionInhibitorComponent, ToggleLockEvent>(OnToggleLockReceived);
}

public override void Update(float frameTime)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Отступ

{
base.Update(frameTime);

var now = _timing.CurTime;
var query = EntityQueryEnumerator<AggressionInhibitorComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (now < comp.NextUpdate || !comp.IsActive || comp.WearingEntity == null)
continue;

if (!RemoveInhibitor(uid, comp))
continue;

Dirty(uid, comp);
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

private void OnInteractUsing(EntityUid uid, AggressionInhibitorComponent comp, InteractUsingEvent args)
{
var user = args.User;

if (!TryComp<TransformComponent>(uid, out var xform))
return;

var parent = xform.ParentUid;

if (!parent.IsValid() && HasComp<ContainerManagerComponent>(parent))
{
PlaybackDenySound(uid, comp);

args.Handled = true;
return;
}

if (!_idCard.TryFindIdCard(args.Used, out var idCard) ||
!TryComp<AccessComponent>(idCard.Owner, out var accessComp))
return;

if (comp.IsLocked)
{
if (GetHasUnlockAccess(comp, accessComp.Tags))
{
if (!RemoveInhibitor(uid, comp))
return;

args.Handled = true;
return;
}
else
PlaybackDenySound(uid, comp);
}
else
{
if (GetHasLockAccess(comp, accessComp.Tags))
{
if (!ActivateInhibitor(uid, parent, comp, user))
return;

args.Handled = true;
return;
}
else
PlaybackDenySound(uid, comp);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

public void OpenDialog(EntityUid uid, AggressionInhibitorComponent comp, EntityUid user)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

OpenDialog/ToggleLock: используйте Entity<AggressionInhibitorComponent> вместо раздельных uid+comp.

Оба публичных метода вызываются из OnOpenDialogReceived/OnToggleLockReceived, где пара uid+comp уже приходит вместе — по гайдлайну стоит свернуть в Entity<AggressionInhibitorComponent>.

As per coding guidelines: "Prefer Entity<T?> over parallel (EntityUid uid, T component) parameters when the call site already has the pair."

Also applies to: 231-231

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Content.Server/_Arcane/AggressionInhibitor/Systems/AggressionInhibitorSystem.cs`
at line 171, Update the public OpenDialog and ToggleLock methods to accept a
single Entity<AggressionInhibitorComponent> parameter instead of separate
EntityUid and AggressionInhibitorComponent parameters. Adjust
OnOpenDialogReceived and OnToggleLockReceived call sites and method internals to
access the entity and component through the new wrapper while preserving
existing behavior.

Source: Coding guidelines

{
EntityUid? parent = _containerSystem.TryGetContainingContainer((uid, null, null), out var container)
? container.Owner
: null;

var targetEntity = parent ?? uid;
if (!_transformSystem.InRange(user, targetEntity, 2f))
return;

if (!_handsSystem.TryGetActiveItem(user, out var heldItem) ||
!_idCard.TryFindIdCard(heldItem.Value, out var idCard) ||
!TryComp<AccessComponent>(idCard.Owner, out var accessComp))
return;

if (comp.IsLocked)
return;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (!GetHasLockAccess(comp, accessComp.Tags))
{
PlaybackDenySound(uid, comp);
return;
}

if (!_playerManager.TryGetSessionByEntity(user, out var session))
return;

_quickDialog.OpenDialog(session, Loc.GetString("stabikor-dialog-title"), Loc.GetString("stabikor-dialog-field") + "\n", (string input) =>
{
if (!EntityManager.EntityExists(uid) || comp.IsLocked)
return;

if (string.IsNullOrEmpty(input))
{
comp.Duration = 60f;
comp.NextUpdate = _timing.CurTime + TimeSpan.FromSeconds(comp.Duration);

Dirty(uid, comp);
_popup.PopupEntity(Loc.GetString("stabikor-duration-set-cancel-fallback", ("time", 1)), uid, user);
return;
}

if (!int.TryParse(input, out var durationMinutes) || durationMinutes < 1 || durationMinutes > 900)
{
_popup.PopupEntity(Loc.GetString("stabikor-dialog-invalid-range"), user, user, PopupType.SmallCaution);
PlaybackDenySound(uid, comp);
return;
}

comp.Duration = durationMinutes * 60f;
comp.NextUpdate = _timing.CurTime + TimeSpan.FromMinutes(durationMinutes);

_popup.PopupEntity(Loc.GetString("stabikor-duration-set-success", ("time", durationMinutes)), uid, user);
PlaybackUnlockSound(uid, comp);

Dirty(uid, comp);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вынеси это в отдельный метод, OpenDialog итак огромный

});
}

public void ToggleLock(EntityUid uid, AggressionInhibitorComponent comp, EntityUid user)
{
EntityUid? parent = _containerSystem.TryGetContainingContainer((uid, null, null), out var container)
? container.Owner
: null;

var targetEntity = parent ?? uid;
if (!_transformSystem.InRange(user, targetEntity, 2f))
return;

if (!_handsSystem.TryGetActiveItem(user, out var heldItem) ||
!_idCard.TryFindIdCard(heldItem.Value, out var idCard) ||
!TryComp<AccessComponent>(idCard.Owner, out var accessComp))
return;

if (comp.IsLocked)
{
if (GetHasUnlockAccess(comp, accessComp.Tags))
{
if (!RemoveInhibitor(uid, comp))
return;
}
Comment on lines +185 to +187

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Молчаливый отказ при неудачном RemoveInhibitor.

Если доступ есть, но сам RemoveInhibitor не сработал (например, TryUnequip не удался), пользователь не получает никакого фидбека — ни попапа, ни звука. Тот же паттерн в OnInteractUsing (line 135), где Handled всё равно ставится в true при провале снятия. Стоит проиграть DenySound/попап и в этой ветке отказа, а не только в ветке недостатка прав.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Content.Server/_Arcane/AggressionInhibitor/Systems/AggressionInhibitorSystem.cs`
around lines 252 - 254, Обновите ветку отказа после вызова RemoveInhibitor в
системе AggressionInhibitorSystem: при неудачном снятии воспроизводите DenySound
и показывайте пользователю попап, аналогично обработке отказа в OnInteractUsing.
Сохраните немедленный возврат после предоставления обратной связи.

else
PlaybackDenySound(uid, comp);
}
else
{
if (GetHasLockAccess(comp, accessComp.Tags))
{
if (!ActivateInhibitor(uid, parent ?? uid, comp, user))
return;
}
else
PlaybackDenySound(uid, comp);
}
}

private bool ActivateInhibitor(EntityUid uid, EntityUid wearerUid, AggressionInhibitorComponent comp, EntityUid user)
{
if (comp.IsActive)
return false;

if (_inventorySystem.TryGetContainingSlot(uid, out var slotDef))
{
if ((slotDef.SlotFlags & SlotFlags.POCKET) != 0)
return false;

if (_inventorySystem.TryGetSlotEntity(wearerUid, slotDef.Name, out var slotItem) && slotItem == uid)
{
comp.NextUpdate = _timing.CurTime + TimeSpan.FromSeconds(comp.Duration);
comp.IsLocked = true;
comp.IsActive = true;
comp.WearingEntity = wearerUid;
_combatMode.SetInCombatMode(wearerUid, false);

Dirty(uid, comp);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

PlaybackLockSound(uid, comp);

_popup.PopupEntity(Loc.GetString("stabikor-activated-success", ("item", uid), ("user", Name(wearerUid))), uid);

return true;
}
}
PlaybackDenySound(uid, comp);

_popup.PopupEntity(Loc.GetString("stabikor-not-equipped"), uid, user);
return false;
}

private bool RemoveInhibitor(EntityUid uid, AggressionInhibitorComponent comp)
{
if (comp.WearingEntity is not { Valid: true } user)
return false;

if (_containerSystem.TryGetContainingContainer(uid, out var container))
{
if (!_containerSystem.TryRemoveFromContainer(uid, force: true))
return false;

_transformSystem.SetCoordinates(uid, _transformSystem.GetMoverCoordinates(user));
}

comp.NextUpdate = TimeSpan.MaxValue;
comp.IsLocked = false;
comp.IsActive = false;
comp.WearingEntity = null;

Dirty(uid, comp);

PlaybackUnlockSound(uid, comp);

_popup.PopupEntity(Loc.GetString("stabikor-moment-shutdown", ("item", uid)), uid);

return true;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

private static bool GetHasLockAccess(AggressionInhibitorComponent comp, HashSet<Robust.Shared.Prototypes.ProtoId<Shared.Access.AccessLevelPrototype>> cardAccess)
{
return comp.LockAccess.Exists(proto => cardAccess.Contains(proto.Id));
}

private static bool GetHasUnlockAccess(AggressionInhibitorComponent comp, HashSet<Robust.Shared.Prototypes.ProtoId<Shared.Access.AccessLevelPrototype>> cardAccess)
{
return comp.UnlockAccess.Exists(proto => cardAccess.Contains(proto.Id));
}

private void PlaybackDenySound(EntityUid uid, AggressionInhibitorComponent comp)
{
_audio.PlayPvs(comp.DenySound, uid);
}

private void PlaybackUnlockSound(EntityUid uid, AggressionInhibitorComponent comp)
{
_audio.PlayPvs(comp.UnlockSound, uid);
}

private void PlaybackLockSound(EntityUid uid, AggressionInhibitorComponent comp)
{
_audio.PlayPvs(comp.LockSound, uid);
}

private void OnOpenDialogReceived(EntityUid uid, AggressionInhibitorComponent comp, OpenDialogEvent args)
{
OpenDialog(uid, comp, args.User);
}

private void OnToggleLockReceived(EntityUid uid, AggressionInhibitorComponent comp, ToggleLockEvent args)
{
ToggleLock(uid, comp, args.User);
}
}
75 changes: 75 additions & 0 deletions Content.Server/_Arcane/CuttableItem/Systems/CuttableItemSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Content.Server.Radio.EntitySystems;
using Content.Shared.Radio;
using Content.Shared._Arcane.CuttableItem.Components;
using Robust.Shared.Prototypes;
using Content.Shared.CuttableItem;
using Content.Shared.Popups;
using Content.Shared.Inventory;

namespace Content.Server._Arcane.CuttableItem.Systems;

public sealed partial class CuttableItemSystem : EntitySystem
{
[Dependency] private RadioSystem _radio = default!;
[Dependency] private IPrototypeManager _prototypeManager = default!;
[Dependency] private SharedPopupSystem _popup = default!;
[Dependency] private InventorySystem _inventorySystem = default!;
[Dependency] private SharedTransformSystem _transformSystem = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<CuttableItemComponent, CuttableCutEvent>(OnItemCut);
SubscribeLocalEvent<CuttableItemComponent, CuttableDoAfterEvent>(OnCutCompleted);
}

private void OnCutCompleted(EntityUid uid, CuttableItemComponent comp, CuttableDoAfterEvent args)
{
if (args.Cancelled || args.Handled)
return;

args.Handled = true;

var victim = Transform(uid).ParentUid;
if (!victim.IsValid())
return;

if (!_inventorySystem.TryGetSlots(victim, out var slotDefinitions))
return;

foreach (var slotDef in slotDefinitions)
{
if (!_inventorySystem.TryGetSlotEntity(victim, slotDef.Name, out var slotEntity) || slotEntity != uid)
continue;

var target = args.User;

if (!_inventorySystem.TryUnequip(target, victim, slotDef.Name, force: true))
continue;

_transformSystem.AttachToGridOrMap(uid);

var victimCoords = Transform(victim).Coordinates;
_transformSystem.SetCoordinates(uid, victimCoords);

_popup.PopupEntity(Loc.GetString("cuttable-item-broken-moment-popup", ("item", uid)), uid);

var ev = new CuttableCutEvent(target);
RaiseLocalEvent(uid, ev);
}
}

private void OnItemCut(EntityUid uid, CuttableItemComponent comp, CuttableCutEvent args)
{
if (!_prototypeManager.TryIndex(comp.RadioChannel, out var channel))
return;

var userName = Name(args.User);
var userItem = Name(uid);

var message = Loc.GetString(comp.AlertMessage, ("user", userName), ("item", userItem));

_radio.SendRadioMessage(uid, message, channel, uid);
}
}
Loading
Loading