Skip to content

Comments

Hotfix 20.01.2026#1429

Merged
Rxup merged 7 commits intomasterfrom
hotfix-200126
Jan 20, 2026
Merged

Hotfix 20.01.2026#1429
Rxup merged 7 commits intomasterfrom
hotfix-200126

Conversation

@Rxup
Copy link
Owner

@Rxup Rxup commented Jan 20, 2026

Summary by CodeRabbit

Примечания к выпуску

  • New Features

    • Добавлена болезнь «Мокрые руки» с трёхступенчатым прогрессом: падение предметов, смазывание предметов (скольжение), повреждение дыхания; добавлен реагент DryHands и рецепт.
    • Эффект смазывания рук, приводящий к вероятному покрытию предметов смазкой.
    • Идентификаторы у некоторых спавнившихся заключённых теперь корректно настраиваются.
  • Bug Fixes

    • Видимость (псоническая/тёмная/призрачная) теперь обновляется через единый масочный механизм.
    • Наблюдатель получает все радиоканалы по умолчанию.
  • Documentation

    • Добавлены локализации и запись в справочник для новых болезней, эффектов и реагентов.

✏️ Tip: You can customize this high-level summary in your review settings.

@Rxup Rxup requested a review from KayzelW January 20, 2026 11:50
@github-actions github-actions bot added Changes: Localization Can be reviewed or fixed by people who are knowledgeable with translation size/M S: Untriaged labels Jan 20, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 20, 2026

Walkthrough

Перенос видимости на GetVisMaskEvent/RefreshVisibilityMask; добавлены болезнь WetHands с эффектами смазки и выпадения предметов, новые компоненты и эффекты болезни; интеграция чтения бумаги с болезнями; рефакторинг Fugitive/PNV, правки прототипов, локализаций и логики ID-карт SuperPsi.

Changes

Cohort / File(s) Изменение
Видимость и глаз
Content.Server/Backmen/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs, Content.Server/Backmen/Psionics/Invisbility/PsionicInvisibilitySystem.cs, Content.Server/Backmen/Species/Shadowkin/Systems/ShadowkinPowerSystem.DarkSwap.cs, Content.Shared/Backmen/Abilities/Psionics/Items/PsionicItemsSystem.cs, Content.Shared/Ghost/SharedGhostSystem.cs, Content.Server/Ghost/GhostSystem.cs
Убрана EyeMapInit-посредник; добавлены обработчики GetVisMaskEvent; заменено прямое манипулирование видимостью Eye на вызовы RefreshVisibilityMask; обновлены правила видимости для PsionicInvisibility/DarkSwap/Ghost.
Болезни — эффекты и система
Content.Server/Backmen/Disease/DiseaseSystem.cs, Content.Server/Backmen/Disease/Effects/DiseaseApplyLube.cs, Content.Server/Backmen/Disease/Effects/DiseaseDropItems.cs, Content.Server/Backmen/Disease/Effects/DiseaseEffectSystem.cs, Content.Shared/Backmen/Disease/Effects/DiseaseLubeHandsComponent.cs, Content.Shared/EntityEffects/Effects/RemoveDiseaseLubeHandsEntityEffect.cs, Content.Shared/Backmen/Disease/SharedDiseaseSystem.cs, Content.Shared/Paper/PaperSystem.cs
Добавлены эффекты DiseaseApplyLube и DiseaseDropItems, сетевой компонент DiseaseLubeHandsComponent, эффект удаления компонента; подписки на события (включая MobStateChanged и чтение бумаги); интеграция PaperSystem → SharedDiseaseSystem.OnPaperRead.
Прототипы болезни и реагентов
Resources/Prototypes/_Backmen/Diseases/wet_hands.yml, Resources/Prototypes/_Backmen/Reagents/dry_hands.yml, Resources/Prototypes/Recipes/Reactions/medicine.yml, Resources/Prototypes/_Backmen/StatusEffects/psionics.yml
Добавлен прототип WetHands (3 стадии с drop/lube/ущербом), reagent DryHands (RemoveDiseaseLubeHands эффект), реакция DryHands в рецептах, новый статус-эффект StatusEffectPsionicDisabled.
Локализация и гайдбук
Resources/Locale/en-US/backmen/disease/disease-proto.ftl, Resources/Locale/en-US/backmen/disease/disease.ftl, Resources/Locale/en-US/reagents/meta/medicine.ftl, Resources/ServerInfo/Guidebook/Service/MedicinalRecipes.xml, Resources/Locale/ru-RU/backmen/disease/disease-proto.ftl, Resources/Locale/ru-RU/backmen/disease/disease.ftl, Resources/Locale/ru-RU/reagents/meta/medicine.ftl
Добавлены ключи и переводы для Wet Hands, сообщений эффектов, реагента DryHands/desiccant и запись в руководство по рецептам.
Fugitive / таймеры / стартапы
Content.Server/Backmen/Fugitive/FugitiveSystem.cs, Content.Server/Backmen/Fugitive/FugitiveCountdownComponent.cs, Content.Server/Backmen/Eye/EyeStartup.cs
Рефакторинг Fugitive в сторону типизированных Entity-обёрток и MapInit-инициализации; добавлены атрибуты авто-паузы для FugitiveCountdownComponent; удалён EyeStartup и связанная логика.
PNV / ночное видение
Content.Shared/Backmen/Eye/NightVision/Components/NightVisionComponent.cs, Content.Shared/Backmen/Eye/NightVision/Systems/PNVSystem.cs
Добавлено поле IsGranted; проверка по ClothingComponent и флагам вместо слота; управление ActionContainer/действиями, RemCompDeferred и cooldown.
Антаганист SuperPsi
Content.Server/Backmen/Antag/SuperPsi/AutoPsiSystem.cs
Добавлена зависимость от тайминга и метод ConfIdCard для конфигурации ID-карт у спавненных заключённых; вызовы применяются после спавна.
Прототипы сущностей/дверей и наблюдатель
Resources/Prototypes/Entities/Mobs/Player/observer.yml, Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml, Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml, Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml
Удалены PsionicInvisibility/DarkSwapInvisibility из visMask у observer; добавлено ActiveRadio.receiveAllChannels; стеклянные двери получили дополнительные родители (BaseDeltaPressure*).
Статусы и зависимости
Content.Shared/Backmen/Psionics/SharedPsionicsSystem.cs, Content.Server/Backmen/Psionics/Invisbility/PsionicInvisibilitySystem.cs, Content.Shared/Backmen/Abilities/Psionics/Items/PsionicItemsSystem.cs
Переход на Content.Shared.StatusEffectNew и EntProtoId для PsionicsDisabled; замена API применения статуса на TryAddStatusEffectDuration; добавлены вызовы RefreshVisibilityMask при надевании/снятии предметов.

Sequence Diagram(s)

sequenceDiagram
    participant Reader as Player (Reader)
    participant PaperSys as PaperSystem
    participant DiseaseShared as SharedDiseaseSystem
    participant DiseaseSrv as DiseaseSystem
    participant EffectSys as DiseaseEffectSystem
    participant LubeSys as DiseaseLubeHandsSystem

    Reader->>PaperSys: открыть бумагу (BeforeUIOpen)
    PaperSys->>DiseaseShared: OnPaperRead(ent)
    DiseaseShared->>DiseaseSrv: уведомление о чтении
    DiseaseSrv->>EffectSys: сгенерировать эффект (MemeticAmirmir) (25% шанс)
    EffectSys-->>DiseaseSrv: применить эффект, создать/обновить DiseaseLubeHandsComponent
    DiseaseSrv->>LubeSys: компонент присоединён (события AfterInteract)
    Note over LubeSys,Reader: при взаимодействии LubeSys может нанести LubedComponent на предмет
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 Я — заяц, в шубке и в движенье,
Смазал я сундучок вдохновенья.
Вид скроен новым, маски живы,
Руки липки — шутки шивы,
Прыг-прыг, поправим всё за день!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive Название PR не отражает основное содержимое изменений; 'Hotfix 20.01.2026' — это ненаписательное обозначение даты без описания фактических изменений. Рекомендуется уточнить название PR, отражающее ключевые изменения, например: 'Добавить новую болезнь WetHands с эффектами и лечением' или другое описание основного функционала.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hotfix-200126

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Content.Server/Backmen/Fugitive/FugitiveSystem.cs (1)

288-300: Дублирование добавления роли FugitiveMindRole.

Роль FugitiveMindRole добавляется дважды:

  • Строка 288: _roleSystem.MindAddRole(mindId, FugitiveMindRole, mind, true);
  • Строка 299: _roleSystem.MindAddRole(mindId, FugitiveMindRole, mind, true);

Второй вызов на строке 299, вероятно, должен добавлять JobRoleComponent для FugitiveRole, а не дублировать FugitiveMindRole.

🐛 Предлагаемое исправление
         _roleSystem.MindAddRole(mindId, FugitiveMindRole, mind, true);

         _mindSystem.TryAddObjective(mindId, mind, EscapeObjective);

         if (_prototypeManager.TryIndex<JobPrototype>(FugitiveRole, out _))
         {
             if (_roleSystem.MindHasRole<JobRoleComponent>(mindId))
             {
                 _roleSystem.MindRemoveRole<JobRoleComponent>(mindId);
             }

-            _roleSystem.MindAddRole(mindId, FugitiveMindRole, mind, true);
+            _roleSystem.MindAddJobRole(mindId, jobPrototype: FugitiveRole);
         }
🤖 Fix all issues with AI agents
In `@Content.Server/Backmen/Disease/DiseaseSystem.cs`:
- Around line 119-120: Комментарий над условием с вероятностью заболевания не
соответствует коду: комментарий говорит про 25%, а выражение использует
_random.Prob(0.40f) (40%). Исправьте несоответствие в классе DiseaseSystem: либо
обновите комментарий, чтобы отражал 40%, либо измените вызов _random.Prob(...)
на 0.25f чтобы соответствовать 25%; отредактируйте строку с _random.Prob и
сопровождающий комментарий вместе, чтобы они всегда совпадали.
- Around line 69-70: The PaperWriteEvent subscription on DiseaseCarrierComponent
won't fire because the event is raised on the paper entity; update the
SubscribeLocalEvent call in DiseaseSystem to subscribe PaperWriteEvent for
PaperComponent (or otherwise route/forward the event from the paper to the
reader) so OnPaperRead is invoked when paper is written, and change the spawn
chance used in OnPaperRead from 0.40f to 0.25f to match the "25% chance"
comment; ensure you update any references to DiseaseCarrierComponent,
PaperComponent, PaperWriteEvent, and the OnPaperRead handler accordingly.

In `@Content.Server/Backmen/Disease/Effects/DiseaseApplyLube.cs`:
- Around line 74-88: В методе OnAfterInteract (в котором обрабатывается
Entity<DiseaseLubeHandsComponent> и параметр ref AfterInteractEvent args) нужно
добавить проверку достижимости цели: если args.CanReach == false — немедленно
вернуть; это предотвратит применение эффекта к недостижимым/закрытым объектам и
сохранить существующие проверки (ItemComponent, LubedComponent, LubeChance).
- Around line 49-59: The DiseaseApplyLube effect currently adds
DiseaseLubeHandsComponent in DiseaseApplyLube but never removes it on cure; wire
up removal by adding the existing RemoveDiseaseLubeHandsEntityEffect to the
disease prototype's cures list (or alternatively remove
DiseaseLubeHandsComponent when disease stage changes/cured). Update the disease
prototype configuration to include RemoveDiseaseLubeHandsEntityEffect under
cures, or add code in the cure/stage transition handler to
RemoveComp<DiseaseLubeHandsComponent> from the args.DiseasedEntity so the
component is cleaned up when the disease is cured.

In `@Content.Server/Backmen/Disease/Effects/DiseaseDropItems.cs`:
- Around line 32-59: В методе DiseaseDropItems вы сейчас помечаете args.Handled
= true и сразу возвращаете после вызова _handsSystem.TryDrop для активной руки,
даже если TryDrop вернул false; исправьте логику: не устанавливайте args.Handled
заранее и не возвращайте сразу после попытки дропа — вызывайте
_handsSystem.TryDrop для active hand и только если он вернул true установить
args.Handled = true и return; если TryDrop вернул false продолжать проверять
другие руки (цикл по hands.Hands.Keys) и также устанавливать args.Handled = true
и прекращать поиск только при успешном дропе.

In `@Content.Server/Backmen/Fugitive/FugitiveSystem.cs`:
- Around line 101-105: Выполни дедупликацию вызова эффектов: либо перемести
вызов DoSpawnEffects исключительно в обработчик OnSpawned
(GhostRoleSpawnerUsedEvent) и убери его из InitFugitive (MindAddedMessage), либо
добавь булев флаг (например SpawnEffectsDone) в компонент (FugitiveComponent или
FugitiveCountdownComponent) и проверяй его перед вызовом DoSpawnEffects в обоих
местах; при первом вызове устанавливай флаг в true, чтобы последующие вызовы (из
InitFugitive или OnSpawned) были пропущены; обнови места с ссылками на
DoSpawnEffects, OnSpawned, InitFugitive, GhostRoleSpawnerUsedEvent,
MindAddedMessage и FirstMindAdded.

In `@Content.Shared/Backmen/Abilities/Psionics/Items/PsionicItemsSystem.cs`:
- Around line 45-46: Проблема: HasStatusEffect вызывается с uid (шляпа) вместо
цели (игрок), поэтому проверка статуса всегда ложная и
RemComp<PsionicInsulationComponent>(args.Equipee) удаляет компонент неправильно;
исправьте вызов HasStatusEffect чтобы проверять статус
StatusEffectPsionicallyInsulated на args.Equipee (или на entity =
args.Equipee.Value/args.EquipeeId в зависимости от типа), оставив
RemComp<PsionicInsulationComponent>(args.Equipee) без изменений и добавив
необходимую проверку на null/валидность args.Equipee перед вызовом.

In `@Content.Shared/Backmen/Eye/NightVision/Systems/PNVSystem.cs`:
- Around line 45-46: При снятии ночного зрения сбрасывайте nvcomp.IsGranted =
false до вызова UpdateIsNightVision, чтобы избежать "лишнего тика" активного NV;
поправьте оба места в PNVSystem (где сейчас nvcomp.IsGranted = true / компонент
снимается deferred) — установите IsGranted = false перед вызовом
UpdateIsNightVision (или перед удалением компонента) для соответствующих веток
кода.

In `@Resources/Prototypes/_Backmen/Reagents/dry_hands.yml`:
- Around line 9-13: Переименуйте реактивную группу 'Acidic' в семантически
подходящее имя (например 'Medicine' или 'Cure') для записи reactiveEffects, так
как текущая группа предназначена для повреждающих эффектов и не подходит для
лечебного эффекта RemoveDiseaseLubeHandsEntityEffect; откройте запись,
содержащую reactiveEffects с methods: [ Touch ] и эффектом
RemoveDiseaseLubeHandsEntityEffect и замените ключ группы 'Acidic' на выбранное
имя, затем выполните поиск по проекту на вхождения 'Acidic' в контексте
реактивных групп и при необходимости обновите ссылки/документацию, чтобы
согласовать семантику.
🧹 Nitpick comments (6)
Content.Server/Backmen/Antag/SuperPsi/AutoPsiSystem.cs (1)

177-190: Магическое число 40 дублируется, рекомендуется извлечь в константу.

TimeSpan.FromMinutes(40) используется дважды (строки 184 и 188). Для улучшения поддерживаемости и согласованности рекомендуется вынести это значение в константу.

♻️ Предлагаемое исправление
+    private static readonly TimeSpan DefaultSentenceDuration = TimeSpan.FromMinutes(40);
+
     private void ConfIdCard(EntityUid ent)
     {
         if (_idCardSystem.TryFindIdCard(ent, out var idCard))
         {
             if (TryComp<GenpopIdCardComponent>(idCard, out var id))
             {
                 id.Crime = Loc.GetString("genpop-prisoner-id-crime-default");
-                id.SentenceDuration = TimeSpan.FromMinutes(40);
+                id.SentenceDuration = DefaultSentenceDuration;
                 Dirty(idCard, id);
             }
             _idCardSystem.SetPermanent(idCard.Owner, true);
-            _idCardSystem.SetExpireTime(idCard.Owner, TimeSpan.FromMinutes(40) + _timing.CurTime);
+            _idCardSystem.SetExpireTime(idCard.Owner, DefaultSentenceDuration + _timing.CurTime);
         }
     }
Content.Server/Backmen/Ghost/GhostReJoinSystem.cs (1)

212-216: Вероятность 60% - магическое число без документации.

Добавление FugitiveComponent к 60% респавнов призраков — это значительное игровое изменение. Рекомендации:

  1. Вынести значение 0.6f в конфигурируемый параметр (CCVar) для гибкой настройки.
  2. Добавить комментарий, объясняющий игровую логику этого решения.
  3. Убедиться, что это намеренное поведение — игроки, вернувшиеся в раунд как призраки, получают статус беглеца.
♻️ Предлагаемое решение: вынести в CCVar
+    // В CCVars:
+    public static readonly CVarDef<float> GhostRespawnFugitiveChance =
+        CVarDef.Create("ghost.respawn_fugitive_chance", 0.6f, CVar.SERVERONLY);
-        if (_random.Prob(0.6f))
+        if (_random.Prob(_configurationManager.GetCVar(CCVars.GhostRespawnFugitiveChance)))
         {
             EnsureComp<FugitiveComponent>(ev.Mob);
         }
Content.Server/Backmen/Species/Shadowkin/Systems/ShadowkinPowerSystem.DarkSwap.cs (1)

338-345: Подозрительный паттерн с StealthComponent.

Логика удаления, создания, установки и повторного удаления StealthComponent выглядит избыточной и неочевидной. Комментарий "Just to be sure..." указывает на неуверенность в корректности.

Если цель — гарантировать сброс stealth-эффекта, рассмотрите упрощение:

♻️ Предлагаемое исправление
-            // Remove the stealth shader from the entity
-            //if (!HasComp<GhostComponent>(uid))
-            RemComp<StealthComponent>(uid);
-            // Just to be sure...
-            var stealth = EnsureComp<StealthComponent>(uid);
-            _stealth.SetVisibility(uid, 1f, stealth);
-            RemComp<StealthComponent>(uid);
+            // Remove the stealth shader from the entity
+            if (TryComp<StealthComponent>(uid, out var stealth))
+            {
+                _stealth.SetVisibility(uid, 1f, stealth);
+                RemComp<StealthComponent>(uid);
+            }
Content.Server/Backmen/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs (1)

58-75: Аналогичная несогласованность в OnAddCanSeeAll.

Тот же паттерн: RefreshVisibilityMask для psionic-видимости, но ручная обработка DarkSwapInvisibility. Если ShadowkinDarkSwappedComponent имеет собственный обработчик GetVisMaskEvent, эта логика может быть унифицирована.

Content.Shared/Backmen/Eye/NightVision/Components/NightVisionComponent.cs (1)

33-34: Рассмотрите добавление атрибутов для IsGranted.

Поле IsGranted не имеет атрибутов [DataField] и [AutoNetworkedField]. Если это состояние должно сохраняться или синхронизироваться между клиентом и сервером (как IsNightVision на строке 13-14), добавьте соответствующие атрибуты.

♻️ Возможное исправление
-    public bool IsGranted = false;
+    [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+    public bool IsGranted = false;
Resources/Prototypes/Entities/Mobs/Player/observer.yml (1)

63-101: Удалите избыточный список каналов.

Свойство receiveAllChannels: true переопределяет поле Channels, поэтому явный список каналов (строки 65-101) не используется и может быть удалён.

Comment on lines 69 to 70
SubscribeLocalEvent<DiseaseCarrierComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<DiseaseCarrierComponent, PaperWriteEvent>(OnPaperRead);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find the DiseaseSystem.cs file and examine the context around lines 69-70
find . -name "DiseaseSystem.cs" -type f

Repository: Rxup/space-station-14

Length of output: 206


🏁 Script executed:

# Search for PaperWriteEvent and PaperReadEvent definitions
rg -n --type=cs "class PaperWriteEvent|class PaperReadEvent" -A 3

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

# Search for places where these events are raised
rg -n --type=cs "RaiseLocalEvent.*PaperWriteEvent|RaiseLocalEvent.*PaperReadEvent" -B 2 -A 2

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

# Search for subscriptions to these events
rg -n --type=cs "SubscribeLocalEvent.*PaperWriteEvent|SubscribeLocalEvent.*PaperReadEvent" -B 2 -A 2

Repository: Rxup/space-station-14

Length of output: 1125


🏁 Script executed:

# Find PaperWriteEvent definition
rg -n --type=cs "record PaperWriteEvent|class PaperWriteEvent|struct PaperWriteEvent" -A 5

Repository: Rxup/space-station-14

Length of output: 551


🏁 Script executed:

# Find the OnPaperRead handler in DiseaseSystem.cs
rg -n --type=cs "OnPaperRead" -B 2 -A 10

Repository: Rxup/space-station-14

Length of output: 2600


🏁 Script executed:

# Find where PaperWriteEvent is raised
rg -n --type=cs "RaiseLocalEvent.*PaperWriteEvent|EntityManager\.RaiseLocalEvent.*PaperWriteEvent" -B 3 -A 1

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

# Look at the DiseaseSystem.cs file around lines 106-118, 121-123
sed -n '100,130p' ./Content.Server/Backmen/Disease/DiseaseSystem.cs

Repository: Rxup/space-station-14

Length of output: 1069


🏁 Script executed:

# Search for where PaperWriteEvent is raised in the codebase
rg -n --type=cs "new PaperWriteEvent|RaiseLocalEvent.*new PaperWriteEvent" -B 5 -A 2

Repository: Rxup/space-station-14

Length of output: 607


🏁 Script executed:

# Look for PaperSystem to understand when PaperWriteEvent is raised
rg -n --type=cs "PaperWriteEvent" Content.Shared/Paper/PaperSystem.cs -B 3 -A 3

Repository: Rxup/space-station-14

Length of output: 1135


🏁 Script executed:

# Check the ActivateOnPaperOpenedComponent subscription to understand the pattern
sed -n '45,60p' Content.Shared/Paper/PaperSystem.cs

Repository: Rxup/space-station-14

Length of output: 648


Исправьте подписку на событие и вероятность срабатывания.

Подписка на PaperWriteEvent со стороны DiseaseCarrierComponent никогда не сработает: событие поднимается на сущности бумаги (строка 146 в Content.Shared/Paper/PaperSystem.cs: RaiseLocalEvent(args.Used, ref writeEvent)), а компонент находится на читателе. Нужно либо подписаться на PaperComponent, либо использовать другой механизм перехвата действия пользователя.

Также в комментарии указано "25% chance", но код использует 0.40f (40%), что не соответствует документации.

🤖 Prompt for AI Agents
In `@Content.Server/Backmen/Disease/DiseaseSystem.cs` around lines 69 - 70, The
PaperWriteEvent subscription on DiseaseCarrierComponent won't fire because the
event is raised on the paper entity; update the SubscribeLocalEvent call in
DiseaseSystem to subscribe PaperWriteEvent for PaperComponent (or otherwise
route/forward the event from the paper to the reader) so OnPaperRead is invoked
when paper is written, and change the spawn chance used in OnPaperRead from
0.40f to 0.25f to match the "25% chance" comment; ensure you update any
references to DiseaseCarrierComponent, PaperComponent, PaperWriteEvent, and the
OnPaperRead handler accordingly.

Comment on lines +49 to +59
private void DiseaseApplyLube(Entity<DiseaseCarrierComponent> ent, ref DiseaseEffectArgs<DiseaseApplyLube> args)
{
if(args.Handled)
return;
args.Handled = true;

// Add component that will handle interaction events and store effect parameters
var component = EnsureComp<DiseaseLubeHandsComponent>(args.DiseasedEntity);
component.LubeChance = args.DiseaseEffect.LubeChance;
component.Slips = args.DiseaseEffect.Slips;
component.SlipStrength = args.DiseaseEffect.SlipStrength;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for DiseaseLubeHandsComponent usage and removal
rg -n --type=cs "DiseaseLubeHandsComponent" -B 2 -A 2

# Search for any removal or cleanup related to this component
rg -n --type=cs -i "RemoveDiseaseLubeHands|remove.*lube.*hands|DiseaseLubeHandsComponent.*remove"

# Look for disease cure/removal handlers
rg -n --type=cs "DiseaseCure|RemoveDisease" -B 2 -A 2

Repository: Rxup/space-station-14

Length of output: 20204


🏁 Script executed:

#!/bin/bash
# Search for disease prototype definitions (YAML files)
fd -e yml -e yaml | xargs grep -l "RemoveDiseaseLubeHandsEntityEffect" 2>/dev/null

# Also search for disease prototype definitions and DiseaseApplyLube
fd -e yml -e yaml | xargs grep -l "DiseaseApplyLube" 2>/dev/null

# Check how entity effects are configured in disease system
rg -n "EntityEffect" Content.Shared/Backmen/Disease/ -A 3 -B 3

Repository: Rxup/space-station-14

Length of output: 117


🏁 Script executed:

cat -n Resources/Prototypes/_Backmen/Reagents/dry_hands.yml

Repository: Rxup/space-station-14

Length of output: 623


🏁 Script executed:

#!/bin/bash
# Search for disease prototype files that use DiseaseApplyLube
fd -e yml -e yaml -path "*[Dd]isease*" | xargs grep -l "DiseaseApplyLube" 2>/dev/null

# Also search all YAML files for DiseaseApplyLube
find . -name "*.yml" -o -name "*.yaml" | xargs grep -l "DiseaseApplyLube" 2>/dev/null | head -20

Repository: Rxup/space-station-14

Length of output: 305


🏁 Script executed:

cat -n ./Resources/Prototypes/_Backmen/Diseases/wet_hands.yml

Repository: Rxup/space-station-14

Length of output: 2227


🏁 Script executed:

#!/bin/bash
# Search for how disease system handles curing and cleanup
rg -n "CureDisease" Content.Server/Backmen/Disease/ -A 5 -B 2

# Check if there's an entity effect system and how it works
rg -n "EntityEffect" Content.Server/Backmen/Disease/ -A 3 -B 1

# Look for the DiseaseSystem class to understand cure mechanism
rg -n "class DiseaseSystem" Content.Server/Backmen/Disease/ -A 30

Repository: Rxup/space-station-14

Length of output: 13740


🏁 Script executed:

#!/bin/bash
# Find the cure queue processing to see what happens when a disease is cured
rg -n "_cureQueue" Content.Server/Backmen/Disease/DiseaseSystem.cs -A 10 -B 2

# Look for the Update method or cure processing
rg -n "Update\|_cureQueue.Add" Content.Server/Backmen/Disease/DiseaseSystem.cs -A 15 -B 2

Repository: Rxup/space-station-14

Length of output: 2089


🏁 Script executed:

#!/bin/bash
# Search for where entity effects are applied - are they applied during stage changes or cure?
rg -n "EntityEffect\|RemoveComp.*DiseaseEffect" Content.Server/Backmen/Disease/ -B 3 -A 3

# Check if there's any handling of disease stage progression or removal
rg -n "StageProgression\|stage change\|remove.*effect" Content.Server/Backmen/Disease/DiseaseSystem.cs -B 2 -A 5

# Look at the full Update method to understand the complete flow
sed -n '165,225p' Content.Server/Backmen/Disease/DiseaseSystem.cs

Repository: Rxup/space-station-14

Length of output: 2059


🏁 Script executed:

#!/bin/bash
# Search for how disease effects are applied - when are effects triggered?
rg -n "DiseaseEffect\|effects\|stage" Content.Server/Backmen/Disease/DiseaseSystem.cs -B 2 -A 2 | head -100

# Check if there's any system that applies entity effects on cure
rg -n "RemoveDiseaseLubeHandsEntityEffect" Content.Server/ Content.Shared/ -B 5 -A 5

# Look for how effects are triggered in the stage progression
rg -n "GetStage\|StageProgression" Content.Server/Backmen/Disease/DiseaseSystem.cs -B 3 -A 10

Repository: Rxup/space-station-14

Length of output: 2254


🏁 Script executed:

#!/bin/bash
# Check how disease effects are applied - look for the disease effect system
rg -n "DiseaseEffectArgs\|DiseaseApplyLube" Content.Server/Backmen/Disease/DiseaseSystem.cs -B 3 -A 5

# Look at what triggers disease effects
rg -n "RaiseLocalEvent.*DiseaseEffect" Content.Server/Backmen/Disease/ -B 2 -A 2

# Check if disease has any cure effects defined
rg -n "class.*Effect\|DiseaseEffect" Content.Server/Backmen/Disease/Effects/ -l

# Look more specifically at DiseaseApplyLube to understand the pattern
cat -n Content.Server/Backmen/Disease/Effects/DiseaseApplyLube.cs | head -80

Repository: Rxup/space-station-14

Length of output: 3276


🏁 Script executed:

#!/bin/bash
# Check if entity effects are supposed to be applied on cure - search for cure effects
rg -n "cure.*effect\|effect.*cure" Content.Server/Backmen/Disease/ -i -B 2 -A 2

# Search disease prototype to see if there are cure effects
rg -n "cure.*effect" Resources/Prototypes/_Backmen/Diseases/ -B 3 -A 3

# Check DiseasePrototype definition for cure effects
rg -n "class DiseasePrototype\|cures\|effects" Content.Shared/Backmen/Disease/DiseasePrototype.cs -B 1 -A 3

Repository: Rxup/space-station-14

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Look at DiseasePrototype to understand the full structure
cat -n Content.Shared/Backmen/Disease/DiseasePrototype.cs | head -100

# Check how disease stages and effects work
rg -n "stage\|Stage" Content.Shared/Backmen/Disease/DiseasePrototype.cs -B 2 -A 2

Repository: Rxup/space-station-14

Length of output: 3665


🏁 Script executed:

#!/bin/bash
# Check if there's a cure effects system or if effects are applied on cure
rg -n "cure.*effect\|CureEffect" Content.Server/Backmen/Disease/ Content.Shared/Backmen/Disease/ -i

# Search for where effects are actually triggered - look at DiseaseEffectSystem
rg -n "class DiseaseEffectSystem" Content.Server/Backmen/Disease/ -A 50 | head -80

# Check if there's any mechanism to run effects on cure
fd -e cs | xargs grep -l "DiseaseEffect\|cure" | xargs rg -n "RaiseLocalEvent.*DiseaseEffect" -B 3 -A 3

Repository: Rxup/space-station-14

Length of output: 8509


🏁 Script executed:

#!/bin/bash
# Check if wet_hands disease has any cure effects defined
cat -n Resources/Prototypes/_Backmen/Diseases/wet_hands.yml

# Search for any cure effects in disease prototypes
rg -n "cureEffect\|cure.*effect" Resources/Prototypes/_Backmen/Diseases/ -i -B 2 -A 2

# Check how disease cures work - do they trigger entity effects?
rg -n "RaiseLocalEvent.*Cure\|cure.*effect" Content.Server/Backmen/Disease/ -i -B 2 -A 3

Repository: Rxup/space-station-14

Length of output: 2227


🏁 Script executed:

#!/bin/bash
# Check if stage downgrade/progression removes effects
rg -n "stage.*change\|progression\|downgrade" Content.Server/Backmen/Disease/DiseaseSystem.cs -i -B 3 -A 5

# Look at where disease effects are applied - they're applied each tick?
rg -n "foreach.*effect\|RaiseLocalEvent.*DiseaseEffect" Content.Server/Backmen/Disease/DiseaseSystem.cs -B 2 -A 3

# Get the full picture of the Update method to see effect application
sed -n '230,280p' Content.Server/Backmen/Disease/DiseaseSystem.cs

Repository: Rxup/space-station-14

Length of output: 1609


🏁 Script executed:

#!/bin/bash
# Continue reading the Process method to see where disease effects are applied
sed -n '280,320p' Content.Server/Backmen/Disease/DiseaseSystem.cs

Repository: Rxup/space-station-14

Length of output: 1114


Добавьте удаление компонента при излечении болезни.

RemoveDiseaseLubeHandsEntityEffect существует, но не настроен в прототипе болезни. При излечении WetHands компонент DiseaseLubeHandsComponent остаётся навсегда. Добавьте эффект удаления в раздел cures протокола болезни или обработайте удаление при смене стадии болезни.

🤖 Prompt for AI Agents
In `@Content.Server/Backmen/Disease/Effects/DiseaseApplyLube.cs` around lines 49 -
59, The DiseaseApplyLube effect currently adds DiseaseLubeHandsComponent in
DiseaseApplyLube but never removes it on cure; wire up removal by adding the
existing RemoveDiseaseLubeHandsEntityEffect to the disease prototype's cures
list (or alternatively remove DiseaseLubeHandsComponent when disease stage
changes/cured). Update the disease prototype configuration to include
RemoveDiseaseLubeHandsEntityEffect under cures, or add code in the cure/stage
transition handler to RemoveComp<DiseaseLubeHandsComponent> from the
args.DiseasedEntity so the component is cleaned up when the disease is cured.

Comment on lines +74 to +88
private void OnAfterInteract(Entity<DiseaseLubeHandsComponent> entity, ref AfterInteractEvent args)
{
if (args.Handled || args.Target is not { Valid: true } target)
return;

// DiseaseLubeHandsComponent should only apply to entities with ItemComponent
if (!HasComp<ItemComponent>(target))
return;

// Don't apply to already lubed items
if (HasComp<LubedComponent>(target))
return;

if (!_random.Prob(entity.Comp.LubeChance))
return;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Добавьте проверку достижимости цели при взаимодействии.

Без args.CanReach можно “смазывать” предметы на дистанции/через препятствия, т.к. AfterInteractEvent может приходить и при недостижимой цели.

🩹 Вариант правки
         if (args.Handled || args.Target is not { Valid: true } target)
             return;
+        if (!args.CanReach)
+            return;
🤖 Prompt for AI Agents
In `@Content.Server/Backmen/Disease/Effects/DiseaseApplyLube.cs` around lines 74 -
88, В методе OnAfterInteract (в котором обрабатывается
Entity<DiseaseLubeHandsComponent> и параметр ref AfterInteractEvent args) нужно
добавить проверку достижимости цели: если args.CanReach == false — немедленно
вернуть; это предотвратит применение эффекта к недостижимым/закрытым объектам и
сохранить существующие проверки (ItemComponent, LubedComponent, LubeChance).

Comment on lines +32 to +59
private void DiseaseDropItems(Entity<DiseaseCarrierComponent> ent, ref DiseaseEffectArgs<DiseaseDropItems> args)
{
if(args.Handled)
return;
args.Handled = true;

if (!_random.Prob(args.DiseaseEffect.DropChance))
return;

if (!TryComp<HandsComponent>(args.DiseasedEntity, out var hands))
return;

Entity<HandsComponent?> handsEntity = (args.DiseasedEntity, hands);

// Try to drop from active hand first, then any hand
if (hands.ActiveHandId != null && _handsSystem.TryGetHeldItem(handsEntity, hands.ActiveHandId, out _))
{
_handsSystem.TryDrop(handsEntity, hands.ActiveHandId, checkActionBlocker: false);
return;
}

// Drop from any hand
foreach (var handId in hands.Hands.Keys)
{
if (_handsSystem.TryGetHeldItem(handsEntity, handId, out _))
{
_handsSystem.TryDrop(handsEntity, handId, checkActionBlocker: false);
break;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Не прерывайте обработку, если TryDrop не удался.

Сейчас при активной руке вы вызываете TryDrop и сразу выходите, даже если дроп не произошёл. Это может свести эффект на нет, хотя в других руках есть предметы. Лучше возвращаться только при успешном дропе и выставлять Handled именно тогда.

🩹 Вариант правки
-        if(args.Handled)
+        if (args.Handled)
             return;
-        args.Handled = true;
 
         if (!_random.Prob(args.DiseaseEffect.DropChance))
             return;
@@
         // Try to drop from active hand first, then any hand
         if (hands.ActiveHandId != null && _handsSystem.TryGetHeldItem(handsEntity, hands.ActiveHandId, out _))
         {
-            _handsSystem.TryDrop(handsEntity, hands.ActiveHandId, checkActionBlocker: false);
-            return;
+            if (_handsSystem.TryDrop(handsEntity, hands.ActiveHandId, checkActionBlocker: false))
+            {
+                args.Handled = true;
+                return;
+            }
         }
@@
         foreach (var handId in hands.Hands.Keys)
         {
             if (_handsSystem.TryGetHeldItem(handsEntity, handId, out _))
             {
-                _handsSystem.TryDrop(handsEntity, handId, checkActionBlocker: false);
-                break;
+                if (_handsSystem.TryDrop(handsEntity, handId, checkActionBlocker: false))
+                {
+                    args.Handled = true;
+                    break;
+                }
             }
         }
🤖 Prompt for AI Agents
In `@Content.Server/Backmen/Disease/Effects/DiseaseDropItems.cs` around lines 32 -
59, В методе DiseaseDropItems вы сейчас помечаете args.Handled = true и сразу
возвращаете после вызова _handsSystem.TryDrop для активной руки, даже если
TryDrop вернул false; исправьте логику: не устанавливайте args.Handled заранее и
не возвращайте сразу после попытки дропа — вызывайте _handsSystem.TryDrop для
active hand и только если он вернул true установить args.Handled = true и
return; если TryDrop вернул false продолжать проверять другие руки (цикл по
hands.Hands.Keys) и также устанавливать args.Handled = true и прекращать поиск
только при успешном дропе.

Comment on lines 45 to 46
if (!_statusEffects.HasStatusEffect(uid, "StatusEffectPsionicallyInsulated"))
RemComp<PsionicInsulationComponent>(args.Equipee);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Баг: проверка статус-эффекта выполняется на неправильной сущности.

Метод HasStatusEffect вызывается с uid (сущность шляпы), но статус-эффект StatusEffectPsionicallyInsulated применяется к игроку (args.Equipee). Это приведёт к тому, что проверка всегда будет возвращать false, и компонент PsionicInsulationComponent будет удаляться всегда, даже если у игрока есть другой источник псионической изоляции через статус-эффект.

🐛 Предлагаемое исправление
-        if (!_statusEffects.HasStatusEffect(uid, "StatusEffectPsionicallyInsulated"))
+        if (!_statusEffects.HasStatusEffect(args.Equipee, "StatusEffectPsionicallyInsulated"))
             RemComp<PsionicInsulationComponent>(args.Equipee);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!_statusEffects.HasStatusEffect(uid, "StatusEffectPsionicallyInsulated"))
RemComp<PsionicInsulationComponent>(args.Equipee);
if (!_statusEffects.HasStatusEffect(args.Equipee, "StatusEffectPsionicallyInsulated"))
RemComp<PsionicInsulationComponent>(args.Equipee);
🤖 Prompt for AI Agents
In `@Content.Shared/Backmen/Abilities/Psionics/Items/PsionicItemsSystem.cs` around
lines 45 - 46, Проблема: HasStatusEffect вызывается с uid (шляпа) вместо цели
(игрок), поэтому проверка статуса всегда ложная и
RemComp<PsionicInsulationComponent>(args.Equipee) удаляет компонент неправильно;
исправьте вызов HasStatusEffect чтобы проверять статус
StatusEffectPsionicallyInsulated на args.Equipee (или на entity =
args.Equipee.Value/args.EquipeeId в зависимости от типа), оставив
RemComp<PsionicInsulationComponent>(args.Equipee) без изменений и добавив
необходимую проверку на null/валидность args.Equipee перед вызовом.

Comment on lines +45 to +46
nvcomp.IsGranted = true;

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Сброс IsGranted перед UpdateIsNightVision на снятии.
Сейчас UpdateIsNightVision вызывается при IsGranted=true, а компонент снимается deferred — возможен “лишний тик” активного NV.

🛠️ Предлагаемое исправление
-        if(!nvcomp.IsGranted)
-            return;
-
-        _nightvisionableSystem.UpdateIsNightVision(args.Equipee, nvcomp);
+        if (!nvcomp.IsGranted)
+            return;
+        nvcomp.IsGranted = false;
+        _nightvisionableSystem.UpdateIsNightVision(args.Equipee, nvcomp);

Also applies to: 71-72

🤖 Prompt for AI Agents
In `@Content.Shared/Backmen/Eye/NightVision/Systems/PNVSystem.cs` around lines 45
- 46, При снятии ночного зрения сбрасывайте nvcomp.IsGranted = false до вызова
UpdateIsNightVision, чтобы избежать "лишнего тика" активного NV; поправьте оба
места в PNVSystem (где сейчас nvcomp.IsGranted = true / компонент снимается
deferred) — установите IsGranted = false перед вызовом UpdateIsNightVision (или
перед удалением компонента) для соответствующих веток кода.

Comment on lines +9 to +13
reactiveEffects:
Acidic:
methods: [ Touch ]
effects:
- !type:RemoveDiseaseLubeHandsEntityEffect
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== Searching for Touch method usage with different groups ==="
rg -n "methods:.*Touch" --type yaml -A 2 -B 2 | head -60

echo -e "\n=== Searching for Acidic group usage ==="
rg -n "Acidic:" --type yaml -A 5 | head -80

echo -e "\n=== Searching for Extinguish group usage ==="
rg -n "Extinguish:" --type yaml -A 5 | head -60

echo -e "\n=== Searching for RemoveDisease effects ==="
rg -n "RemoveDisease" --type yaml | head -40

Repository: Rxup/space-station-14

Length of output: 14401


Переименуйте реактивную группу с Acidic на более подходящую.

Группа Acidic в проекте последовательно используется для повреждающих реакций с эффектами HealthChange. Применение её к лечебному эффекту RemoveDiseaseLubeHandsEntityEffect противоречит сложившемуся паттерну. Группа Extinguish предназначена для огня/холода и также не подходит. Рекомендуется выбрать или создать семантически подходящую группу для лечебных/противодействующих эффектов (например, Medicine или Cure), которая отражает назначение эффекта.

🤖 Prompt for AI Agents
In `@Resources/Prototypes/_Backmen/Reagents/dry_hands.yml` around lines 9 - 13,
Переименуйте реактивную группу 'Acidic' в семантически подходящее имя (например
'Medicine' или 'Cure') для записи reactiveEffects, так как текущая группа
предназначена для повреждающих эффектов и не подходит для лечебного эффекта
RemoveDiseaseLubeHandsEntityEffect; откройте запись, содержащую reactiveEffects
с methods: [ Touch ] и эффектом RemoveDiseaseLubeHandsEntityEffect и замените
ключ группы 'Acidic' на выбранное имя, затем выполните поиск по проекту на
вхождения 'Acidic' в контексте реактивных групп и при необходимости обновите
ссылки/документацию, чтобы согласовать семантику.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Content.Server/Backmen/Fugitive/FugitiveSystem.cs (1)

282-294: Дублирование вызова MindAddRole с идентичными параметрами.

На строке 282 вызывается _roleSystem.MindAddRole(mindId, FugitiveMindRole, mind, true) безусловно. Затем на строке 293 внутри блока if (_prototypeManager.TryIndex<JobPrototype>(FugitiveRole, out _)) выполняется идентичный вызов той же функции с теми же параметрами.

Логика блока if (удаление JobRoleComponent и повторное добавление FugitiveMindRole) выглядит ошибочной. Вероятно, вторая строка либо избыточна и должна быть удалена, либо должна добавлять JobRoleComponent с ролью FugitiveRole, а не повторно добавлять FugitiveMindRole.

🧹 Nitpick comments (1)
Content.Server/Backmen/Fugitive/FugitiveSystem.cs (1)

296-300: LGTM!

Вызов DoSpawnEffects(uid.AsNullable()) корректен. Защита через FirstMindAdded гарантирует однократное выполнение эффектов.

Мелочь: опечатка в комментарии — "seperate" вместо "separate".

@Rxup Rxup merged commit e9bef78 into master Jan 20, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Changes: Localization Can be reviewed or fixed by people who are knowledgeable with translation S: Untriaged size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant