-
Notifications
You must be signed in to change notification settings - Fork 69
[Feature] New aggression inhibitor collar #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
24b7b6a
96aff60
d230173
f569cb0
0d8645d
3c11247
49951e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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.
Sorry, something went wrong. |
||
|
|
||
| public override void Initialize() | ||
| { | ||
| base.Initialize(); | ||
|
|
||
| SubscribeLocalEvent<AggressionInhibitorComponent, InteractUsingEvent>(OnInteractUsing); | ||
| SubscribeLocalEvent<AggressionInhibitorComponent, OpenDialogEvent>(OnOpenDialogReceived); | ||
| SubscribeLocalEvent<AggressionInhibitorComponent, ToggleLockEvent>(OnToggleLockReceived); | ||
| } | ||
|
|
||
| public override void Update(float frameTime) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
| } | ||
| } | ||
|
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); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| public void OpenDialog(EntityUid uid, AggressionInhibitorComponent comp, EntityUid user) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Оба публичных метода вызываются из As per coding guidelines: "Prefer Also applies to: 231-231 🤖 Prompt for AI AgentsSource: 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; | ||
|
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); | ||
|
coderabbitai[bot] marked this conversation as resolved.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Молчаливый отказ при неудачном Если доступ есть, но сам 🤖 Prompt for AI Agents |
||
| 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); | ||
|
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; | ||
| } | ||
|
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); | ||
| } | ||
| } | ||
| 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); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тут кролик накидал очень много полезных комментариев.
Из важного:
Перенести проверку боевоего режима в shared, а тут оставить всё остальное.
При отказе в доступе нету попапа или какой либо причины в отказе.
Ну и ещё исправления.