diff --git a/Content.Server/_Sunrise/Biocode/Systems/BiocodeDeactivationSystem.cs b/Content.Server/_Sunrise/Biocode/Systems/BiocodeDeactivationSystem.cs
new file mode 100644
index 0000000000..056ad731ef
--- /dev/null
+++ b/Content.Server/_Sunrise/Biocode/Systems/BiocodeDeactivationSystem.cs
@@ -0,0 +1,46 @@
+using Content.Shared._Sunrise.Biocode.Systems;
+using Content.Shared.Pinpointer;
+using Content.Server.Popups;
+using Content.Server.Pinpointer;
+
+namespace Content.Server._Sunrise.Biocode.Systems;
+
+///
+/// Server-side implementation of biocode deactivation system.
+///
+public sealed class ServerBiocodeDeactivationSystem : BiocodeDeactivationSystem
+{
+ [Dependency] private readonly PopupSystem _popup = default!;
+ [Dependency] private readonly PinpointerSystem _pinpointerSystem = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+
+ private static readonly ISawmill Sawmill = Logger.GetSawmill("biocode.deactivation");
+
+ protected override void ShowAlert(EntityUid user, string alertText)
+ {
+ _popup.PopupEntity(Loc.GetString(alertText), user, user);
+ }
+
+ protected override void DeactivateItem(EntityUid uid)
+ {
+ // Handle pinpointer deactivation using the proper system method
+ if (TryComp(uid, out var pinpointer))
+ {
+ // Always ensure the pinpointer is deactivated
+ _pinpointerSystem.SetActive(uid, false, pinpointer);
+
+ // Force update appearance to ensure visual state is correct
+ if (TryComp(uid, out var appearance))
+ {
+ _appearance.SetData(uid, PinpointerVisuals.IsActive, false, appearance);
+ _appearance.SetData(uid, PinpointerVisuals.TargetDistance, Distance.Unknown, appearance);
+ }
+ }
+
+ // Add other item types here as needed
+ // Example: if (TryComp(uid, out var otherComponent))
+ // {
+ // _someOtherSystem.Deactivate(uid, otherComponent);
+ // }
+ }
+}
diff --git a/Content.Server/_Sunrise/BloodCult/BloodCultTargetComponent.cs b/Content.Server/_Sunrise/BloodCult/BloodCultTargetComponent.cs
deleted file mode 100644
index 024df13779..0000000000
--- a/Content.Server/_Sunrise/BloodCult/BloodCultTargetComponent.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Content.Server._Sunrise.BloodCult;
-
-[RegisterComponent]
-public sealed partial class BloodCultTargetComponent : Component
-{
-}
diff --git a/Content.Server/_Sunrise/BloodCult/Commands/AddCultTargetCommand.cs b/Content.Server/_Sunrise/BloodCult/Commands/AddCultTargetCommand.cs
new file mode 100644
index 0000000000..82990b0acd
--- /dev/null
+++ b/Content.Server/_Sunrise/BloodCult/Commands/AddCultTargetCommand.cs
@@ -0,0 +1,61 @@
+using Content.Server._Sunrise.BloodCult.GameRule;
+using Content.Server.Administration;
+using Content.Shared.Administration;
+using Robust.Shared.Console;
+using Robust.Server.Player;
+
+namespace Content.Server._Sunrise.BloodCult.Commands;
+
+[AdminCommand(AdminFlags.Admin)]
+public sealed class AddCultTargetCommand : IConsoleCommand
+{
+ [Dependency] private readonly IEntityManager _entManager = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+
+ public string Command => "bloodcult_addtarget";
+ public string Description => Loc.GetString("bloodcult-addtarget-description");
+ public string Help => Loc.GetString("bloodcult-addtarget-help");
+
+ public void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ if (args.Length != 1)
+ {
+ shell.WriteError(Loc.GetString("bloodcult-addtarget-usage"));
+ return;
+ }
+
+ var ckey = args[0];
+ if (!_playerManager.TryGetSessionByUsername(ckey, out var session) || session.AttachedEntity == null)
+ {
+ shell.WriteError(Loc.GetString("bloodcult-addtarget-player-not-found", ("ckey", ckey)));
+ return;
+ }
+
+ var entityUid = session.AttachedEntity.Value;
+
+ if (!_entManager.EntitySysManager.TryGetEntitySystem(out var cultRuleSystem))
+ {
+ shell.WriteError(Loc.GetString("bloodcult-addtarget-system-not-found"));
+ return;
+ }
+
+ var rule = cultRuleSystem.GetRule();
+ if (rule == null)
+ {
+ shell.WriteError(Loc.GetString("bloodcult-addtarget-rule-not-found"));
+ return;
+ }
+
+ // Use the system method to add target properly
+ if (!cultRuleSystem.AddSpecificCultTarget(entityUid, rule))
+ {
+ shell.WriteError(Loc.GetString("bloodcult-addtarget-already-target"));
+ return;
+ }
+
+ var targetName = _entManager.TryGetComponent(entityUid, out var meta)
+ ? meta.EntityName
+ : Loc.GetString("bloodcult-unknown-entity");
+ shell.WriteLine(Loc.GetString("bloodcult-addtarget-success", ("name", targetName)));
+ }
+}
diff --git a/Content.Server/_Sunrise/BloodCult/Commands/ListCultTargetsCommand.cs b/Content.Server/_Sunrise/BloodCult/Commands/ListCultTargetsCommand.cs
new file mode 100644
index 0000000000..388e910086
--- /dev/null
+++ b/Content.Server/_Sunrise/BloodCult/Commands/ListCultTargetsCommand.cs
@@ -0,0 +1,42 @@
+using Content.Server._Sunrise.BloodCult.GameRule;
+using Content.Server.Administration;
+using Content.Shared.Administration;
+using Robust.Shared.Console;
+
+namespace Content.Server._Sunrise.BloodCult.Commands;
+
+[AdminCommand(AdminFlags.Admin)]
+public sealed class ListCultTargetsCommand : IConsoleCommand
+{
+ [Dependency] private readonly IEntityManager _entManager = default!;
+
+ public string Command => "bloodcult_listtargets";
+ public string Description => Loc.GetString("bloodcult-listtargets-description");
+ public string Help => Loc.GetString("bloodcult-listtargets-help");
+
+ public void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ if (!_entManager.EntitySysManager.TryGetEntitySystem(out var cultRuleSystem))
+ {
+ shell.WriteError(Loc.GetString("bloodcult-listtargets-system-not-found"));
+ return;
+ }
+
+ var rule = cultRuleSystem.GetRule();
+ if (rule?.CultTargets == null || rule.CultTargets.Count == 0)
+ {
+ shell.WriteLine(Loc.GetString("bloodcult-listtargets-no-targets"));
+ return;
+ }
+
+ shell.WriteLine(Loc.GetString("bloodcult-listtargets-header", ("count", rule.CultTargets.Count)));
+ foreach (var (target, isSacrificed) in rule.CultTargets)
+ {
+ var targetName = _entManager.TryGetComponent(target, out var meta)
+ ? meta.EntityName
+ : Loc.GetString("bloodcult-unknown-entity");
+ var status = isSacrificed ? Loc.GetString("bloodcult-listtargets-sacrificed") : Loc.GetString("bloodcult-listtargets-alive");
+ shell.WriteLine(Loc.GetString("bloodcult-listtargets-target", ("name", targetName), ("uid", target), ("status", status)));
+ }
+ }
+}
diff --git a/Content.Server/_Sunrise/BloodCult/Commands/RemoveCultTargetCommand.cs b/Content.Server/_Sunrise/BloodCult/Commands/RemoveCultTargetCommand.cs
new file mode 100644
index 0000000000..03ab1bdaf1
--- /dev/null
+++ b/Content.Server/_Sunrise/BloodCult/Commands/RemoveCultTargetCommand.cs
@@ -0,0 +1,61 @@
+using Content.Server._Sunrise.BloodCult.GameRule;
+using Content.Server.Administration;
+using Content.Shared.Administration;
+using Robust.Shared.Console;
+using Robust.Server.Player;
+
+namespace Content.Server._Sunrise.BloodCult.Commands;
+
+[AdminCommand(AdminFlags.Admin)]
+public sealed class RemoveCultTargetCommand : IConsoleCommand
+{
+ [Dependency] private readonly IEntityManager _entManager = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+
+ public string Command => "bloodcult_removetarget";
+ public string Description => Loc.GetString("bloodcult-removetarget-description");
+ public string Help => Loc.GetString("bloodcult-removetarget-help");
+
+ public void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ if (args.Length != 1)
+ {
+ shell.WriteError(Loc.GetString("bloodcult-removetarget-usage"));
+ return;
+ }
+
+ var ckey = args[0];
+ if (!_playerManager.TryGetSessionByUsername(ckey, out var session) || session.AttachedEntity == null)
+ {
+ shell.WriteError(Loc.GetString("bloodcult-removetarget-player-not-found", ("ckey", ckey)));
+ return;
+ }
+
+ var entityUid = session.AttachedEntity.Value;
+
+ if (!_entManager.EntitySysManager.TryGetEntitySystem(out var cultRuleSystem))
+ {
+ shell.WriteError(Loc.GetString("bloodcult-removetarget-system-not-found"));
+ return;
+ }
+
+ var rule = cultRuleSystem.GetRule();
+ if (rule == null)
+ {
+ shell.WriteError(Loc.GetString("bloodcult-removetarget-rule-not-found"));
+ return;
+ }
+
+ // Use the system method to remove target properly
+ if (!cultRuleSystem.RemoveSpecificCultTarget(entityUid, rule))
+ {
+ shell.WriteError(Loc.GetString("bloodcult-removetarget-not-target"));
+ return;
+ }
+
+ var targetName = _entManager.TryGetComponent(entityUid, out var meta)
+ ? meta.EntityName
+ : Loc.GetString("bloodcult-unknown-entity");
+ shell.WriteLine(Loc.GetString("bloodcult-removetarget-success", ("name", targetName)));
+ }
+}
diff --git a/Content.Server/_Sunrise/BloodCult/GameRule/BloodCultRuleSystem.cs b/Content.Server/_Sunrise/BloodCult/GameRule/BloodCultRuleSystem.cs
index fbd43e9aa3..835d1c9236 100644
--- a/Content.Server/_Sunrise/BloodCult/GameRule/BloodCultRuleSystem.cs
+++ b/Content.Server/_Sunrise/BloodCult/GameRule/BloodCultRuleSystem.cs
@@ -142,6 +142,45 @@ public sealed class BloodCultRuleSystem : GameRuleSystem
}
}
+ ///
+ /// Manually add a specific entity as a cult target.
+ ///
+ public bool AddSpecificCultTarget(EntityUid target, BloodCultRuleComponent rule)
+ {
+ if (!Exists(target) || rule.CultTargets.ContainsKey(target))
+ return false;
+
+ rule.CultTargets.Add(target, false);
+ EnsureComp(target);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var killCultistTargetsComponent))
+ {
+ _cultistTargetsConditionSystem.RefresTitle(uid, rule.CultTargets, killCultistTargetsComponent);
+ }
+ return true;
+ }
+
+ ///
+ /// Manually remove a specific entity as a cult target.
+ ///
+ public bool RemoveSpecificCultTarget(EntityUid target, BloodCultRuleComponent rule)
+ {
+ if (!rule.CultTargets.ContainsKey(target))
+ return false;
+
+ rule.CultTargets.Remove(target);
+ if (Exists(target))
+ RemComp(target);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var killCultistTargetsComponent))
+ {
+ _cultistTargetsConditionSystem.RefresTitle(uid, rule.CultTargets, killCultistTargetsComponent);
+ }
+ return true;
+ }
+
protected override void Added(EntityUid uid,
BloodCultRuleComponent component,
GameRuleComponent gameRule,
diff --git a/Content.Shared/Pinpointer/SharedPinpointerSystem.cs b/Content.Shared/Pinpointer/SharedPinpointerSystem.cs
index 4710960183..2f62c5d9fc 100644
--- a/Content.Shared/Pinpointer/SharedPinpointerSystem.cs
+++ b/Content.Shared/Pinpointer/SharedPinpointerSystem.cs
@@ -67,7 +67,7 @@ public abstract class SharedPinpointerSystem : EntitySystem
private void OnExamined(EntityUid uid, PinpointerComponent component, ExaminedEvent args)
{
- if (!args.IsInDetailsRange || component.TargetName == null)
+ if (!args.IsInDetailsRange || component.TargetName == null || !component.IsActive)
return;
args.PushMarkup(Loc.GetString("examine-pinpointer-linked", ("target", component.TargetName)));
diff --git a/Content.Shared/_Sunrise/Biocode/Components/BiocodeDeactivationComponent.cs b/Content.Shared/_Sunrise/Biocode/Components/BiocodeDeactivationComponent.cs
new file mode 100644
index 0000000000..22dd415fef
--- /dev/null
+++ b/Content.Shared/_Sunrise/Biocode/Components/BiocodeDeactivationComponent.cs
@@ -0,0 +1,29 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._Sunrise.Biocode.Components;
+
+///
+/// Component that automatically deactivates items when they're not in the possession of authorized users.
+///
+[RegisterComponent, NetworkedComponent]
+public sealed partial class BiocodeDeactivationComponent : Component
+{
+ ///
+ /// Whether the item should be deactivated when removed from authorized user's possession.
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public bool DeactivateOnRemoval = true;
+
+ ///
+ /// Whether the item should be deactivated when placed in unauthorized user's possession.
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public bool DeactivateOnUnauthorized = true;
+
+ ///
+ /// Alert text to show when unauthorized user tries to use the item.
+ /// If null, uses the BiocodeComponent's alert text.
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public string? AlertText;
+}
diff --git a/Content.Shared/_Sunrise/Biocode/Systems/BiocodeDeactivationSystem.cs b/Content.Shared/_Sunrise/Biocode/Systems/BiocodeDeactivationSystem.cs
new file mode 100644
index 0000000000..c44d181637
--- /dev/null
+++ b/Content.Shared/_Sunrise/Biocode/Systems/BiocodeDeactivationSystem.cs
@@ -0,0 +1,136 @@
+using Content.Shared._Sunrise.Biocode.Components;
+using Content.Shared.Containers;
+using Content.Shared.Hands.Components;
+using Content.Shared.Inventory;
+using Content.Shared.Storage.Components;
+using Content.Shared.Interaction;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Storage;
+using Content.Shared.Hands;
+using Content.Shared.Inventory.Events;
+using Robust.Shared.Containers;
+
+namespace Content.Shared._Sunrise.Biocode.Systems;
+
+///
+/// System that handles automatic deactivation of biocoded items when they're not in authorized user's possession.
+///
+public abstract class BiocodeDeactivationSystem : EntitySystem
+{
+ [Dependency] private readonly BiocodeSystem _biocodeSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnActivate);
+ SubscribeLocalEvent(OnUseInHand);
+ SubscribeLocalEvent(OnItemDropped);
+ SubscribeLocalEvent(OnItemPickedUp);
+ }
+
+ private void OnItemDropped(EntityUid uid, BiocodeDeactivationComponent component, DroppedEvent args)
+ {
+ if (!component.DeactivateOnRemoval)
+ return;
+
+ // Check if this item has biocode
+ if (!TryComp(uid, out var biocodeComponent))
+ return;
+
+ // Item was dropped, deactivate it
+ DeactivateItem(uid);
+ }
+
+ private void OnItemPickedUp(EntityUid uid, BiocodeDeactivationComponent component, GotEquippedEvent args)
+ {
+ if (!component.DeactivateOnUnauthorized)
+ return;
+
+ // Check if this item has biocode
+ if (!TryComp(uid, out var biocodeComponent))
+ return;
+
+ // Check if the picker is authorized
+ if (_biocodeSystem.CanUse(args.Equipee, biocodeComponent.Factions))
+ return;
+
+ // Picker is not authorized, deactivate the item
+ DeactivateItem(uid);
+ }
+
+ private void OnActivate(EntityUid uid, BiocodeDeactivationComponent component, ActivateInWorldEvent args)
+ {
+ if (args.Handled || !args.Complex)
+ return;
+
+ // Check if this item has biocode
+ if (!TryComp(uid, out var biocodeComponent))
+ return;
+
+ // Check if user is authorized
+ if (_biocodeSystem.CanUse(args.User, biocodeComponent.Factions))
+ return;
+
+ // User is not authorized, show alert and prevent activation
+ var alertText = component.AlertText ?? biocodeComponent.AlertText;
+ ShowAlert(args.User, alertText);
+ args.Handled = true;
+ }
+
+ private void OnUseInHand(EntityUid uid, BiocodeDeactivationComponent component, UseInHandEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ // Check if this item has biocode
+ if (!TryComp(uid, out var biocodeComponent))
+ return;
+
+ // Check if user is authorized
+ if (_biocodeSystem.CanUse(args.User, biocodeComponent.Factions))
+ return;
+
+ // User is not authorized, show alert and prevent use
+ var alertText = component.AlertText ?? biocodeComponent.AlertText;
+ ShowAlert(args.User, alertText);
+ args.Handled = true;
+ }
+
+ ///
+ /// Shows an alert to the user. Override this method to implement specific alert display logic.
+ ///
+ protected abstract void ShowAlert(EntityUid user, string alertText);
+
+ ///
+ /// Deactivates the item. Override this method in the shared system to implement specific deactivation logic.
+ ///
+ protected abstract void DeactivateItem(EntityUid uid);
+
+ private EntityUid? GetContainerOwner(EntityUid container)
+ {
+ // Try to find the owner through various container types
+ if (TryComp(container, out _))
+ {
+ return container;
+ }
+
+ if (TryComp(container, out _))
+ {
+ return container;
+ }
+
+ if (TryComp(container, out _))
+ {
+ return container;
+ }
+
+ // Check if this container is inside another entity
+ var parent = Transform(container).ParentUid;
+ if (parent != EntityUid.Invalid)
+ {
+ return GetContainerOwner(parent);
+ }
+
+ return null;
+ }
+}
diff --git a/Content.Shared/_Sunrise/BloodCult/Components/BloodCultTargetComponent.cs b/Content.Shared/_Sunrise/BloodCult/Components/BloodCultTargetComponent.cs
new file mode 100644
index 0000000000..6ff7e43fc7
--- /dev/null
+++ b/Content.Shared/_Sunrise/BloodCult/Components/BloodCultTargetComponent.cs
@@ -0,0 +1,11 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared._Sunrise.BloodCult.Components;
+
+///
+/// Component that marks an entity as a target for the Blood cult.
+///
+[RegisterComponent, NetworkedComponent]
+public sealed partial class BloodCultTargetComponent : Component
+{
+}
diff --git a/Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/devices/blood_cult_pinpointer.ftl b/Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/devices/blood_cult_pinpointer.ftl
new file mode 100644
index 0000000000..a34c2e3625
--- /dev/null
+++ b/Resources/Locale/en-US/_prototypes/_sunrise/entities/objects/devices/blood_cult_pinpointer.ftl
@@ -0,0 +1,2 @@
+ent-BloodCultPinpointer = Blood Locator
+ .desc = A sinister looking pinpointer with a strange crystal lodged into it. It points towards cult targets. Only cultists know about this device and how to use it, for others it's a strange piece of junk.
diff --git a/Resources/Locale/en-US/_strings/_sunrise/bloodcult/commands.ftl b/Resources/Locale/en-US/_strings/_sunrise/bloodcult/commands.ftl
new file mode 100644
index 0000000000..f9608b089b
--- /dev/null
+++ b/Resources/Locale/en-US/_strings/_sunrise/bloodcult/commands.ftl
@@ -0,0 +1,36 @@
+# Blood Cult Console Commands
+
+# Add Target Command
+bloodcult-addtarget-description = Add target for Blood Cult
+bloodcult-addtarget-help = Adds a specific player as a target for the Blood Cult. The player will be marked for tracking and potential sacrifice.
+bloodcult-addtarget-usage = Usage: bloodcult_addtarget
+bloodcult-addtarget-player-not-found = Player with ckey '{ $ckey }' not found or not in game.
+bloodcult-addtarget-system-not-found = Blood Cult system not found.
+bloodcult-addtarget-rule-not-found = No active Blood Cult rule found.
+bloodcult-addtarget-already-target = Entity is already a cult target.
+bloodcult-addtarget-success = Successfully added { $name } as a cult target.
+
+# Remove Target Command
+bloodcult-removetarget-description = Remove target for Blood Cult
+bloodcult-removetarget-help = Removes a specific player from the Blood Cult target list. The player will no longer be tracked or marked for sacrifice.
+bloodcult-removetarget-usage = Usage: bloodcult_removetarget
+bloodcult-removetarget-player-not-found = Player with ckey '{ $ckey }' not found or not in game.
+bloodcult-removetarget-system-not-found = Blood Cult system not found.
+bloodcult-removetarget-rule-not-found = No active Blood Cult rule found.
+bloodcult-removetarget-not-target = Entity is not a cult target.
+bloodcult-removetarget-success = Successfully removed { $name } as a cult target.
+
+# List Targets Command
+bloodcult-listtargets-description = Show all current Blood Cult targets
+bloodcult-listtargets-help = Displays all current Blood Cult targets with their status (alive or sacrificed) and entity information.
+bloodcult-listtargets-usage = Usage: bloodcult_listtargets
+bloodcult-listtargets-system-not-found = Blood Cult system not found.
+bloodcult-listtargets-no-targets = No cult targets found.
+bloodcult-listtargets-header = Current cult targets ({ $count }):
+bloodcult-listtargets-sacrificed = Sacrificed
+bloodcult-listtargets-alive = Alive
+bloodcult-listtargets-target = { $name } ({ $uid }) - { $status }
+bloodcult-unknown-entity = Unknown Entity
+
+# Cult Device Alert
+bloodcult-biocode-alert = The device pulses with dark energy, rejecting your touch. Only those bound by blood may wield its power.
diff --git a/Resources/Locale/en-US/_strings/_sunrise/bloodcult/pinpointer.ftl b/Resources/Locale/en-US/_strings/_sunrise/bloodcult/pinpointer.ftl
new file mode 100644
index 0000000000..6ec7defb6d
--- /dev/null
+++ b/Resources/Locale/en-US/_strings/_sunrise/bloodcult/pinpointer.ftl
@@ -0,0 +1,11 @@
+#Sunrise-start
+pinpointer-cult-target = cult target
+pinpointer-cult-no-target = No cult targets found
+pinpointer-cult-target-name = { $name } ({ $status })
+pinpointer-cult-target-alive = Alive
+pinpointer-cult-target-sacrificed = Sacrificed
+
+# Blood Locator
+blood-locator-name = Blood Locator
+blood-locator-description = A sinister looking pinpointer with a strange crystal lodged into it. It points towards cult targets. Only cultists know about this device and how to use it, for others it's a strange piece of junk.
+#Sunrise-End
diff --git a/Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/devices/blood_cult_pinpointer.ftl b/Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/devices/blood_cult_pinpointer.ftl
new file mode 100644
index 0000000000..afbdd54548
--- /dev/null
+++ b/Resources/Locale/ru-RU/_prototypes/_sunrise/entities/objects/devices/blood_cult_pinpointer.ftl
@@ -0,0 +1,2 @@
+ent-BloodCultPinpointer = Кровеуказатель
+ .desc = Зловещий указатель со странным кристаллом, встроенным в него. Он указывает на цели культа. Только культисты знают об этом устройстве и как его использовать, для остальных это странный хлам.
diff --git a/Resources/Locale/ru-RU/_strings/_sunrise/bloodcult/commands.ftl b/Resources/Locale/ru-RU/_strings/_sunrise/bloodcult/commands.ftl
new file mode 100644
index 0000000000..c1cc1d6a0f
--- /dev/null
+++ b/Resources/Locale/ru-RU/_strings/_sunrise/bloodcult/commands.ftl
@@ -0,0 +1,40 @@
+# Blood Cult Console Commands
+
+# Add Target Command
+bloodcult-addtarget-description = Добавить цель для Культа Крови
+bloodcult-addtarget-help = Добавляет конкретного игрока как цель Культа Крови для отслеживания и потенциального жертвоприношения.
+bloodcult-addtarget-usage = Использование: bloodcult_addtarget
+bloodcult-addtarget-player-not-found = Игрок с ckey '{ $ckey }' не найден или не в игре.
+bloodcult-addtarget-system-not-found = Система Культа Крови не найдена.
+bloodcult-addtarget-rule-not-found = Активное правило Культа Крови не найдено.
+bloodcult-addtarget-already-target = Сущность уже является целью культа.
+bloodcult-addtarget-success = Цель культа { $name } успешно добавлена.
+
+# Remove Target Command
+bloodcult-removetarget-description = Удалить цель для Культа Крови
+bloodcult-removetarget-help = Удаляет конкретного игрока из списка целей Культа Крови, прекращая отслеживание и отметку для жертвоприношения.
+bloodcult-removetarget-usage = Использование: bloodcult_removetarget
+bloodcult-removetarget-player-not-found = Игрок с ckey '{ $ckey }' не найден или не в игре.
+bloodcult-removetarget-system-not-found = Система Культа Крови не найдена.
+bloodcult-removetarget-rule-not-found = Активное правило Культа Крови не найдено.
+bloodcult-removetarget-not-target = Сущность не является целью культа.
+bloodcult-removetarget-success = Цель культа { $name } успешно удалена.
+
+# List Targets Command
+bloodcult-listtargets-description = Показать все текущие цели Культа Крови
+bloodcult-listtargets-help = Отображает все текущие цели Культа Крови с их статусом (жив или принесен в жертву) и информацией о сущности.
+bloodcult-listtargets-usage = Использование: bloodcult_listtargets
+bloodcult-listtargets-system-not-found = Система Культа Крови не найдена.
+bloodcult-listtargets-no-targets = Цели культа не найдены.
+bloodcult-listtargets-header = { $count ->
+ [1] Текущая цель культа ({ $count }):
+ [few] Текущие цели культа ({ $count }):
+ *[other] Текущие цели культа ({ $count }):
+}
+bloodcult-listtargets-sacrificed = Принесен в жертву
+bloodcult-listtargets-alive = Жив
+bloodcult-listtargets-target = { $name } ({ $uid }) - { $status }
+bloodcult-unknown-entity = Неизвестная сущность
+
+# Cult Device Alert
+bloodcult-biocode-alert = Устройство пульсирует тёмной энергией, отвергая ваше прикосновение. Только те, кто связан кровью, могут владеть его силой.
diff --git a/Resources/Locale/ru-RU/_strings/_sunrise/bloodcult/pinpointer.ftl b/Resources/Locale/ru-RU/_strings/_sunrise/bloodcult/pinpointer.ftl
new file mode 100644
index 0000000000..53fb4ac537
--- /dev/null
+++ b/Resources/Locale/ru-RU/_strings/_sunrise/bloodcult/pinpointer.ftl
@@ -0,0 +1,9 @@
+pinpointer-cult-target = цель культа
+pinpointer-cult-no-target = Цели культа не найдены
+pinpointer-cult-target-name = { $name } ({ $status })
+pinpointer-cult-target-alive = Жив
+pinpointer-cult-target-sacrificed = Принесен в жертву
+
+# Blood Locator
+blood-locator-name = Кровеуказатель
+blood-locator-description = Зловещий указатель со странным кристаллом, встроенным в него. Он указывает на цели культа. Только культисты знают об этом устройстве и как его использовать, для остальных это странный хлам.
diff --git a/Resources/Prototypes/_Sunrise/BloodCult/Entities/Objects/Devices/blood_cult_pinpointer.yml b/Resources/Prototypes/_Sunrise/BloodCult/Entities/Objects/Devices/blood_cult_pinpointer.yml
new file mode 100644
index 0000000000..0c5584b4fd
--- /dev/null
+++ b/Resources/Prototypes/_Sunrise/BloodCult/Entities/Objects/Devices/blood_cult_pinpointer.yml
@@ -0,0 +1,45 @@
+- type: entity
+ name: ent-BloodCultPinpointer
+ description: ent-BloodCultPinpointer-desc
+ id: BloodCultPinpointer
+ parent: PinpointerBase
+ components:
+ - type: Sprite
+ sprite: _Sunrise/BloodCult/pinpointer.rsi
+ layers:
+ - state: pinpointer_bloodcult
+ map: ["enum.PinpointerLayers.Base"]
+ - state: pinonnull
+ map: ["enum.PinpointerLayers.Screen"]
+ shader: unshaded
+ visible: false
+ - type: Item
+ inhandVisuals:
+ left:
+ - state: inhand-left-base
+ color: "#8b0000"
+ - state: inhand-left-stripe
+ color: "#8b0000"
+ - state: inhand-left-top
+ right:
+ - state: inhand-right-base
+ color: "#8b0000"
+ - state: inhand-right-stripe
+ color: "#8b0000"
+ - state: inhand-right-top
+ - type: Icon
+ sprite: _Sunrise/BloodCult/pinpointer.rsi
+ state: pinpointer_bloodcult
+ - type: Pinpointer
+ component: BloodCultTarget
+ targetName: cult target
+ updateTargetName: true
+ canRetarget: false
+ - type: Biocode
+ factions:
+ - BloodCult
+ alertText: bloodcult-biocode-alert
+ - type: BiocodeDeactivation
+ deactivateOnRemoval: true
+ deactivateOnUnauthorized: true
+ alertText: bloodcult-biocode-alert
diff --git a/Resources/Prototypes/_Sunrise/BloodCult/cult_types.yml b/Resources/Prototypes/_Sunrise/BloodCult/cult_types.yml
index 6b70ae4a73..c9da9524dc 100644
--- a/Resources/Prototypes/_Sunrise/BloodCult/cult_types.yml
+++ b/Resources/Prototypes/_Sunrise/BloodCult/cult_types.yml
@@ -8,6 +8,7 @@
startingItems:
- NarsieRitualDagger
- CultRunicMetal10
+ - BloodCultPinpointer
- type: bloodCult
id: NarbeeCult
@@ -19,6 +20,7 @@
startingItems:
- NarbeeRitualDagger
- CultRunicMetal10
+ - BloodCultPinpointer
- type: bloodCult
id: ReaperCult
@@ -30,3 +32,4 @@
startingItems:
- ReaperRitualDagger
- CultRunicMetal10
+ - BloodCultPinpointer
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/meta.json b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/meta.json
new file mode 100644
index 0000000000..7400b8a2a0
--- /dev/null
+++ b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/meta.json
@@ -0,0 +1,142 @@
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "© 2025 Alexnov33x",
+ "states": [
+ {
+ "name": "pinonalert",
+ "directions": 8,
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinonalertdirect",
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinonalertnull",
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinonclose",
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinondirect",
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinondirectlarge",
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinondirectsmall",
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinondirectxtrlarge",
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinonfar",
+ "delays": [
+ [
+ 0.6,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinonmedium",
+ "delays": [
+ [
+ 0.4,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinonnull",
+ "delays": [
+ [
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "pinpointer_bloodcult"
+ }
+ ]
+}
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonalert.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonalert.png
new file mode 100644
index 0000000000..39e73ee266
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonalert.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonalertdirect.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonalertdirect.png
new file mode 100644
index 0000000000..6e53ef3a3b
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonalertdirect.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonalertnull.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonalertnull.png
new file mode 100644
index 0000000000..1556d82c76
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonalertnull.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonclose.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonclose.png
new file mode 100644
index 0000000000..d652e4cd02
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonclose.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirect.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirect.png
new file mode 100644
index 0000000000..c2ac6e44e2
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirect.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirectlarge.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirectlarge.png
new file mode 100644
index 0000000000..c8b1fa4875
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirectlarge.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirectsmall.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirectsmall.png
new file mode 100644
index 0000000000..ab7940efd9
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirectsmall.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirectxtrlarge.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirectxtrlarge.png
new file mode 100644
index 0000000000..5db693402c
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinondirectxtrlarge.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonfar.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonfar.png
new file mode 100644
index 0000000000..fd0fbf51ac
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonfar.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonmedium.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonmedium.png
new file mode 100644
index 0000000000..5f8e89aec2
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonmedium.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonnull.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonnull.png
new file mode 100644
index 0000000000..a309d053e5
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinonnull.png differ
diff --git a/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinpointer_bloodcult.png b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinpointer_bloodcult.png
new file mode 100644
index 0000000000..5d9c8e05c5
Binary files /dev/null and b/Resources/Textures/_Sunrise/BloodCult/pinpointer.rsi/pinpointer_bloodcult.png differ