From 809d3ddad029124e27653bc5a971debd2567a0c1 Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Mon, 2 Mar 2026 18:56:04 +0300
Subject: [PATCH 01/11] =?UTF-8?q?[ADD]=20=D0=A1=D0=B8=D1=81=D1=82=D0=B5?=
=?UTF-8?q?=D0=BC=D0=B0=20=D0=B0=D0=BB=D0=BB=D0=B5=D1=80=D0=B3=D0=B8=D0=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../UI/HealthAnalyzerWindow.xaml | 3 +
.../UI/HealthAnalyzerWindow.xaml.cs | 6 +
Content.Server/ADT/Body/AllergicSystem.cs | 75 ++++++++
.../EntitySystems/DiseaseDiagnoserSystem.cs | 169 ++++++++++++++++++
.../Body/Systems/MetabolizerSystem.cs | 13 +-
.../Botany/Components/BotanySwabComponent.cs | 11 +-
.../Botany/Systems/BotanySwabSystem.cs | 73 +++++++-
.../ADT/Body/Allergies/AlergicComponent.cs | 12 ++
.../ADT/EntityEffects/PurgeAllergies.cs | 50 ++++++
.../ADT/Medical/DiseaseDiagnoserComponent.cs | 35 ++++
.../Locale/ru-RU/ADT/allergies/allergies.ftl | 9 +
.../ru-RU/ADT/guidebook/chemistry/effects.ftl | 5 +
.../ru-RU/ADT/reagents/meta/medicine.ftl | 3 +
.../Prototypes/ADT/Reagents/medicine.yml | 18 ++
.../ADT/Recipes/Reactions/medicine.yml | 12 +-
.../Machines/Medical/disease_diagnoser.yml | 27 +++
16 files changed, 512 insertions(+), 9 deletions(-)
create mode 100644 Content.Server/ADT/Body/AllergicSystem.cs
create mode 100644 Content.Server/ADT/Medical/EntitySystems/DiseaseDiagnoserSystem.cs
create mode 100644 Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
create mode 100644 Content.Shared/ADT/EntityEffects/PurgeAllergies.cs
create mode 100644 Content.Shared/ADT/Medical/DiseaseDiagnoserComponent.cs
create mode 100644 Resources/Locale/ru-RU/ADT/allergies/allergies.ftl
diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml
index 159e6db8576..bdd4ced7520 100644
--- a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml
+++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml
@@ -50,6 +50,9 @@
+
+
+
diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs
index db30bfab6b5..231ebe27cfc 100644
--- a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs
+++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs
@@ -22,6 +22,7 @@
using Robust.Client.ResourceManagement;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
+using Content.Shared.ADT.Body.Allergies;
namespace Content.Client.HealthAnalyzer.UI
{
@@ -131,6 +132,11 @@ public void Populate(HealthAnalyzerScannedUserMessage msg)
DamageLabel.Text = damageable.TotalDamage.ToString();
+ // ADT-Tweak-Start
+ bool allergic = _entityManager.HasComponent(target.Value);
+ AllergyText.Visible = allergic;
+ // ADT-Tweak-End
+
// Alerts
var showAlerts = msg.Unrevivable == true || msg.Bleeding == true;
diff --git a/Content.Server/ADT/Body/AllergicSystem.cs b/Content.Server/ADT/Body/AllergicSystem.cs
new file mode 100644
index 00000000000..33f5ff9791b
--- /dev/null
+++ b/Content.Server/ADT/Body/AllergicSystem.cs
@@ -0,0 +1,75 @@
+using System.Linq;
+using Content.Server.Body.Systems;
+using Content.Shared.ADT.Body.Allergies;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Damage;
+using Content.Shared.Damage.Prototypes;
+using Content.Shared.EntityEffects.Effects;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+
+namespace Content.Server.ADT.Body;
+
+public sealed class AllergicSystem : EntitySystem
+{
+ [Dependency] private readonly IRobustRandom _random = default!;
+
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+
+ private ProtoId _allergyDamageGroup = "Poison";
+
+ private DamageTypePrototype? _allergyDamageTypeProto;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnInit);
+ SubscribeLocalEvent(OnGetReagentEffects);
+
+ _allergyDamageTypeProto = _proto.Index(_allergyDamageGroup);
+ }
+
+ private List> GetRandomAllergies(int min, int max)
+ {
+ var reagentsCount = _proto.Count();
+
+ if (reagentsCount == 0)
+ return new();
+
+ int allergiesCount = _random.Next(min, max);
+
+ List picked = Enumerable.Range(0, allergiesCount).Select(_ => _random.Next(reagentsCount)).ToList();
+ var index = 0;
+
+ List> allergies = new();
+
+ foreach (var proto in _proto.EnumeratePrototypes())
+ {
+ if (picked.Contains(index))
+ allergies.Add(proto.ID);
+ index++;
+ }
+
+ return allergies;
+ }
+
+ public void OnInit(EntityUid uid, AllergicComponent component, ComponentInit args)
+ {
+ component.Triggers = GetRandomAllergies(3, 5);
+ }
+
+ public void OnGetReagentEffects(EntityUid uid, AllergicComponent component, ref GetReagentEffectsEvent ev)
+ {
+ if (!component.Triggers.Contains(ev.Reagent.Prototype))
+ return;
+
+ var damageSpecifier = new DamageSpecifier(_allergyDamageTypeProto!, 0.5);
+ var damageEffect = new HealthChange
+ {
+ Damage = damageSpecifier
+ };
+
+ ev.Effects = ev.Effects.Append(damageEffect).ToArray();
+ }
+}
diff --git a/Content.Server/ADT/Medical/EntitySystems/DiseaseDiagnoserSystem.cs b/Content.Server/ADT/Medical/EntitySystems/DiseaseDiagnoserSystem.cs
new file mode 100644
index 00000000000..4a67f8b8173
--- /dev/null
+++ b/Content.Server/ADT/Medical/EntitySystems/DiseaseDiagnoserSystem.cs
@@ -0,0 +1,169 @@
+using System.Linq;
+using System.Text;
+using Content.Server.Botany;
+using Content.Server.Power.EntitySystems;
+using Content.Shared.ADT.Medical;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Interaction;
+using Content.Shared.Paper;
+using Content.Shared.Popups;
+using Content.Shared.Power;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Containers;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
+
+namespace Content.Server.ADT.Medical.EntitySystems;
+
+public sealed class DiseaseDiagnoserSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedContainerSystem _container = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly PaperSystem _paperSystem = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnActivateInWorld);
+ SubscribeLocalEvent(OnRemoveAttempt);
+ SubscribeLocalEvent(OnPowerChanged);
+ }
+
+ #region Events
+
+ private void OnActivateInWorld(Entity entity, ref ActivateInWorldEvent args)
+ {
+ if (args.Handled || !args.Complex)
+ return;
+
+ TryStart(entity, args.User);
+ args.Handled = true;
+ }
+
+ private void OnRemoveAttempt(Entity ent, ref ContainerIsRemovingAttemptEvent args)
+ {
+ if (args.Container.ID == ent.Comp.ContainerId && ent.Comp.Working)
+ args.Cancel();
+ }
+
+ private void OnPowerChanged(Entity ent, ref PowerChangedEvent args)
+ {
+ if (!args.Powered)
+ Stop(ent);
+ }
+
+ #endregion
+
+ #region Lifecycle
+
+ public void TryStart(Entity entity, EntityUid? user)
+ {
+ var (uid, comp) = entity;
+ if (comp.Working)
+ return;
+
+ if (!HasPower(entity))
+ {
+ if (user != null)
+ _popup.PopupClient(Loc.GetString("solution-container-mixer-no-power"), entity, user.Value);
+ return;
+ }
+
+ if (!_container.TryGetContainer(uid, comp.ContainerId, out var container) || container.Count == 0)
+ {
+ if (user != null)
+ _popup.PopupClient(Loc.GetString("solution-container-mixer-popup-nothing-to-mix"), entity, user.Value);
+ return;
+ }
+
+ comp.Working = true;
+ comp.WorkingSoundEntity = _audio.PlayPvs(comp.WorkingSound, entity, comp.WorkingSound?.Params.WithLoop(true));
+ comp.WorkTimeEnd = _timing.CurTime + comp.WorkDuration;
+ _appearance.SetData(entity, DiseaseDiagnoserVisuals.Printing, true);
+ Dirty(uid, comp);
+ }
+
+ public void Stop(Entity entity)
+ {
+ var (uid, comp) = entity;
+ if (!comp.Working)
+ return;
+ _audio.Stop(comp.WorkingSoundEntity);
+ _appearance.SetData(entity, DiseaseDiagnoserVisuals.Printing, false);
+ comp.Working = false;
+ comp.WorkingSoundEntity = null;
+ Dirty(uid, comp);
+ }
+
+ public string GetReport(BotanySwabComponent swabComponent)
+ {
+ StringBuilder builder = new();
+
+ if (swabComponent.AllergicTriggers == null || swabComponent.AllergicTriggers.Count() == 0)
+ builder.Append(Loc.GetString("paper-allergy-prefix", ("allergies", Loc.GetString("paper-allergy-no"))));
+ else
+ {
+ List localizedNames = new();
+ foreach (ProtoId trigger in swabComponent.AllergicTriggers)
+ {
+ localizedNames.Add(_proto.Index(trigger).LocalizedName.ToLower());
+ }
+
+ builder.Append(Loc.GetString("paper-allergy-prefix", ("allergies", String.Join(", ", localizedNames))));
+ }
+
+ return builder.ToString();
+ }
+
+ public void Finish(Entity entity)
+ {
+ var (uid, comp) = entity;
+ if (!comp.Working)
+ return;
+ Stop(entity);
+
+ if (!TryComp(entity, out var reactionMixer)
+ || !_container.TryGetContainer(uid, comp.ContainerId, out var container))
+ return;
+
+ var swabEnt = container.ContainedEntities.First();
+ if (!TryComp(swabEnt, out var swabComponent))
+ return;
+
+ var printed = Spawn("DiagnosisReportPaper", Transform(uid).Coordinates);
+
+ if (TryComp(printed, out var paper))
+ {
+ _paperSystem.SetContent((printed, paper), GetReport(swabComponent));
+ }
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var comp))
+ {
+ if (!comp.Working)
+ continue;
+
+ if (_timing.CurTime < comp.WorkTimeEnd)
+ continue;
+
+ Finish((uid, comp));
+ }
+ }
+
+ #endregion
+
+ public bool HasPower(Entity entity)
+ {
+ return this.IsPowered(entity, EntityManager);
+ }
+}
diff --git a/Content.Server/Body/Systems/MetabolizerSystem.cs b/Content.Server/Body/Systems/MetabolizerSystem.cs
index b5b30ae74cf..116af09fafe 100644
--- a/Content.Server/Body/Systems/MetabolizerSystem.cs
+++ b/Content.Server/Body/Systems/MetabolizerSystem.cs
@@ -194,8 +194,15 @@ private void TryMetabolize(Entity
public SeedData? SeedData;
+
+ /// ADT-Tweak-Start
+ ///
+ /// Allergic triggers from players swabbed.
+ ///
+ /// ADT-Tweak-End
+ [DataField]
+ public List>? AllergicTriggers;
}
}
diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs
index e8c7af92c27..c68e8e439c6 100644
--- a/Content.Server/Botany/Systems/BotanySwabSystem.cs
+++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs
@@ -1,5 +1,8 @@
+using System.Linq;
using Content.Server.Botany.Components;
using Content.Server.Popups;
+using Content.Shared.ADT.Body.Allergies;
+using Content.Shared.Body.Components;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Interaction;
@@ -21,6 +24,16 @@ public override void Initialize()
SubscribeLocalEvent(OnDoAfter);
}
+ // ADT-Tweak-Start
+ ///
+ /// If swab was used.
+ ///
+ private bool IsUsed(BotanySwabComponent swab)
+ {
+ return swab.SeedData != null || swab.AllergicTriggers != null;
+ }
+ // ADT-Tweak-End
+
///
/// This handles swab examination text
/// so you can tell if they are used or not.
@@ -29,7 +42,7 @@ private void OnExamined(EntityUid uid, BotanySwabComponent swab, ExaminedEvent a
{
if (args.IsInDetailsRange)
{
- if (swab.SeedData != null)
+ if (IsUsed(swab)) // ADT-Tweak: IsUsed
args.PushMarkup(Loc.GetString("swab-used"));
else
args.PushMarkup(Loc.GetString("swab-unused"));
@@ -41,7 +54,8 @@ private void OnExamined(EntityUid uid, BotanySwabComponent swab, ExaminedEvent a
///
private void OnAfterInteract(EntityUid uid, BotanySwabComponent swab, AfterInteractEvent args)
{
- if (args.Target == null || !args.CanReach || !HasComp(args.Target))
+ if (args.Target == null || !args.CanReach ||
+ !HasComp(args.Target) && !HasComp(args.Target)) // ADT-Tweak
return;
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, swab.SwabDelay, new BotanySwabDoAfterEvent(), uid, target: args.Target, used: uid)
@@ -52,19 +66,23 @@ private void OnAfterInteract(EntityUid uid, BotanySwabComponent swab, AfterInter
});
}
+ // ADT-Tweak-Start
///
/// Save seed data or cross-pollenate.
///
- private void OnDoAfter(EntityUid uid, BotanySwabComponent swab, DoAfterEvent args)
+ private void OnPlantDoAfter(EntityUid uid, BotanySwabComponent swab, PlantHolderComponent plant, DoAfterEvent args)
{
- if (args.Cancelled || args.Handled || !TryComp(args.Args.Target, out var plant))
+ if (swab.AllergicTriggers != null)
+ {
+ _popupSystem.PopupEntity(Loc.GetString("botany-swab-unusable-plant"), args.Args.Target!.Value, args.Args.User);
return;
+ }
if (swab.SeedData == null)
{
// Pick up pollen
swab.SeedData = plant.Seed;
- _popupSystem.PopupEntity(Loc.GetString("botany-swab-from"), args.Args.Target.Value, args.Args.User);
+ _popupSystem.PopupEntity(Loc.GetString("botany-swab-from"), args.Args.Target!.Value, args.Args.User);
}
else
{
@@ -73,9 +91,52 @@ private void OnDoAfter(EntityUid uid, BotanySwabComponent swab, DoAfterEvent arg
return;
plant.Seed = _mutationSystem.Cross(swab.SeedData, old); // Cross-pollenate
swab.SeedData = old; // Transfer old plant pollen to swab
- _popupSystem.PopupEntity(Loc.GetString("botany-swab-to"), args.Args.Target.Value, args.Args.User);
+ _popupSystem.PopupEntity(Loc.GetString("botany-swab-to"), args.Args.Target!.Value, args.Args.User);
+ }
+ }
+ // ADT-Tweak-End
+
+ // ADT-Tweak-Start
+ ///
+ /// Save allergy triggers if exists.
+ ///
+ private void OnCreatureDoAfter(EntityUid uid, BotanySwabComponent swab, DoAfterEvent args)
+ {
+ if (swab.SeedData != null)
+ {
+ _popupSystem.PopupEntity(Loc.GetString("botany-swab-unusable-bio"), args.Args.Target!.Value, args.Args.User);
+ return;
}
+ if (TryComp(args.Args.Target!.Value, out var allergic))
+ if (swab.AllergicTriggers == null)
+ swab.AllergicTriggers = allergic.Triggers;
+ else
+ /** Snowball unique allergies on swab */
+ swab.AllergicTriggers = swab.AllergicTriggers.Concat(allergic.Triggers).Distinct().ToList();
+
+ /** Mark as used anyway */
+ if (swab.AllergicTriggers == null)
+ swab.AllergicTriggers = new();
+
+ _popupSystem.PopupEntity(Loc.GetString("botany-swab-used-bio"), args.Args.Target!.Value, args.Args.User);
+ }
+ // ADT-Tweak-End
+
+ private void OnDoAfter(EntityUid uid, BotanySwabComponent swab, DoAfterEvent args)
+ {
+ if (args.Cancelled || args.Handled)
+ return;
+
+ // ADT-Tweak-Start
+ if (TryComp(args.Args.Target, out var plant))
+ OnPlantDoAfter(uid, swab, plant, args);
+ else if (HasComp(args.Args.Target))
+ OnCreatureDoAfter(uid, swab, args);
+ else
+ return;
+ // ADT-Tweak-End
+
args.Handled = true;
}
}
diff --git a/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs b/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
new file mode 100644
index 00000000000..a34fcaf3b9c
--- /dev/null
+++ b/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
@@ -0,0 +1,12 @@
+using Content.Shared.Chemistry.Reagent;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.ADT.Body.Allergies;
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class AllergicComponent : Component
+{
+ [DataField]
+ public List> Triggers = new();
+}
diff --git a/Content.Shared/ADT/EntityEffects/PurgeAllergies.cs b/Content.Shared/ADT/EntityEffects/PurgeAllergies.cs
new file mode 100644
index 00000000000..589ca06c391
--- /dev/null
+++ b/Content.Shared/ADT/EntityEffects/PurgeAllergies.cs
@@ -0,0 +1,50 @@
+using Content.Shared.ADT.Body.Allergies;
+using Content.Shared.Body.Components;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Reagent;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+///
+/// Эффект для удаления аллергий, аллергены которых присутствуют в крови.
+///
+public sealed partial class PurgeAllergiesEntityEffectSystem : EntityEffectSystem
+{
+ [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
+
+ protected override void Effect(Entity entity, ref EntityEffectEvent args)
+ {
+ if (!TryComp(entity, out var bloodstreamComp))
+ return;
+
+ if (!TryComp(entity, out var solMan))
+ return;
+
+ (EntityUid uid, _) = entity;
+ ref List> allergicTriggers = ref entity.Comp.Triggers;
+
+ if (_solutionContainerSystem.TryGetSolution((uid, solMan), BloodstreamComponent.DefaultChemicalsSolutionName, out _, out var chemicalsSolution))
+ {
+ foreach (var (reagent, _) in chemicalsSolution.Contents)
+ {
+ if (allergicTriggers.Contains(reagent.Prototype))
+ allergicTriggers.Remove(reagent.Prototype);
+ }
+ }
+ }
+}
+
+///
+public sealed partial class PurgeAllergies : EntityEffectBase
+{
+ public override string? EntityEffectGuidebookText(
+ IPrototypeManager prototype,
+ IEntitySystemManager entSys)
+ {
+ return Loc.GetString(
+ "reagent-effect-guidebook-purge-allergies",
+ ("chance", Probability));
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/ADT/Medical/DiseaseDiagnoserComponent.cs b/Content.Shared/ADT/Medical/DiseaseDiagnoserComponent.cs
new file mode 100644
index 00000000000..09bbfde97b7
--- /dev/null
+++ b/Content.Shared/ADT/Medical/DiseaseDiagnoserComponent.cs
@@ -0,0 +1,35 @@
+using Robust.Shared.Audio;
+using Robust.Shared.Audio.Components;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.ADT.Medical;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class DiseaseDiagnoserComponent : Component
+{
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public string ContainerId = "swab";
+
+ [DataField, AutoNetworkedField]
+ public bool Working;
+
+ [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public TimeSpan WorkDuration;
+
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+ public TimeSpan WorkTimeEnd;
+
+ [DataField, AutoNetworkedField]
+ public SoundSpecifier? WorkingSound;
+
+ [ViewVariables]
+ public Entity? WorkingSoundEntity;
+}
+
+[Serializable, NetSerializable]
+public enum DiseaseDiagnoserVisuals : byte
+{
+ Printing
+}
\ No newline at end of file
diff --git a/Resources/Locale/ru-RU/ADT/allergies/allergies.ftl b/Resources/Locale/ru-RU/ADT/allergies/allergies.ftl
new file mode 100644
index 00000000000..71a78ccb8f4
--- /dev/null
+++ b/Resources/Locale/ru-RU/ADT/allergies/allergies.ftl
@@ -0,0 +1,9 @@
+health-analyzer-window-entity-allergy-text = Имеется аллергия
+
+botany-swab-unusable-plant = Палочка непригода для сбора пыльцы.
+botany-swab-unusable-bio = Палочка непригода для взятия биологических образов.
+
+botany-swab-used-bio = Вы собрали биологические образцы.
+
+paper-allergy-prefix = Аллергия: {$allergies}
+paper-allergy-no = нет
\ No newline at end of file
diff --git a/Resources/Locale/ru-RU/ADT/guidebook/chemistry/effects.ftl b/Resources/Locale/ru-RU/ADT/guidebook/chemistry/effects.ftl
index d34a41c5c9a..52143823464 100644
--- a/Resources/Locale/ru-RU/ADT/guidebook/chemistry/effects.ftl
+++ b/Resources/Locale/ru-RU/ADT/guidebook/chemistry/effects.ftl
@@ -8,3 +8,8 @@ reagent-effect-guidebook-teleport =
[1] Вызвать
*[other] вызвать
} неконтролируемое перемещение
+reagent-effect-guidebook-purge-allergies =
+ { $chance ->
+ [1] Лечит
+ *[other] лечат
+ } аллергию
diff --git a/Resources/Locale/ru-RU/ADT/reagents/meta/medicine.ftl b/Resources/Locale/ru-RU/ADT/reagents/meta/medicine.ftl
index dd1c30707e2..13efb5d541e 100644
--- a/Resources/Locale/ru-RU/ADT/reagents/meta/medicine.ftl
+++ b/Resources/Locale/ru-RU/ADT/reagents/meta/medicine.ftl
@@ -54,3 +54,6 @@ reagent-desc-miadinol = Хорошо помогает восстановить
reagent-name-devazigon = Девазигон
reagent-desc-devazigon = Антитоксин выведенный на основе нейротоксина, используется в устранении последствий сильного отравления.
+
+reagent-name-sanallergium = Саналлергин
+reagent-desc-sanallergium = Используется для перманентного снижения имунной реакции пациента к аллергенам в крови.
diff --git a/Resources/Prototypes/ADT/Reagents/medicine.yml b/Resources/Prototypes/ADT/Reagents/medicine.yml
index 5c0a6fa3166..bea20a61f58 100644
--- a/Resources/Prototypes/ADT/Reagents/medicine.yml
+++ b/Resources/Prototypes/ADT/Reagents/medicine.yml
@@ -642,3 +642,21 @@
types:
Cold: 2
Bloodloss: 1
+
+- type: reagent
+ id: ADTSanallergium # Latin "Allegy healer" (source: ChatGPT)
+ name: reagent-name-sanallergium
+ group: Medicine
+ desc: reagent-desc-sanallergium
+ flavor: medicine
+ physicalDesc: reagent-physical-desc-skunky
+ color: "#7ba854"
+ worksOnTheDead: true
+ metabolisms:
+ Medicine:
+ effects:
+ - !type:PurgeAllergies
+ conditions:
+ - !type:ReagentCondition
+ reagent: ADTSanallergium
+ min: 10
\ No newline at end of file
diff --git a/Resources/Prototypes/ADT/Recipes/Reactions/medicine.yml b/Resources/Prototypes/ADT/Recipes/Reactions/medicine.yml
index 57b26404542..5a9de6d0145 100644
--- a/Resources/Prototypes/ADT/Recipes/Reactions/medicine.yml
+++ b/Resources/Prototypes/ADT/Recipes/Reactions/medicine.yml
@@ -252,4 +252,14 @@
Potassium:
amount: 1
products:
- ADTDevazigon: 3
\ No newline at end of file
+ ADTDevazigon: 3
+
+- type: reaction
+ id: ADTSanallergium
+ reactants:
+ Diphenhydramine:
+ amount: 2
+ Dylovene:
+ amount: 1
+ products:
+ ADTSanallergium: 3
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml
index ad98f47e36a..56b0f04ede1 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/disease_diagnoser.yml
@@ -17,6 +17,33 @@
- type: Appearance
- type: Machine
board: DiagnoserMachineCircuitboard
+ # ADT-Tweak-Start
+ - type: GenericVisualizer
+ visuals:
+ enum.DiseaseDiagnoserVisuals.Printing:
+ enum.DiseaseMachineVisualLayers.IsRunning:
+ True: {state: "running"}
+ False: {state: "icon"}
+ enum.PowerDeviceVisuals.Powered:
+ enum.DiseaseMachineVisualLayers.IsOn:
+ True: { visible: True }
+ False: { visible: False }
+ - type: ItemSlots
+ slots:
+ swab:
+ whitelist:
+ components:
+ - BotanySwab
+ - type: ContainerContainer
+ containers:
+ swab: !type:ContainerSlot
+ - type: DiseaseDiagnoser
+ workDuration: 5
+ workingSound:
+ path: /Audio/Machines/spinning.ogg
+ params:
+ volume: -4
+ # ADT-Tweak-End
- type: entity
name: disease diagnoser report
From fde50c2a0e33f4f77b8444dec9274ff4363217d3 Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Mon, 2 Mar 2026 19:15:03 +0300
Subject: [PATCH 02/11] =?UTF-8?q?[TWEAK]=20=D0=A2=D1=80=D0=B5=D0=B9=D1=82?=
=?UTF-8?q?=20=D0=B8=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D1=83=D1=80?=
=?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B2=20=D0=BA=D0=BE=D0=BC=D0=BF?=
=?UTF-8?q?=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Content.Server/ADT/Body/AllergicSystem.cs | 2 +-
.../ADT/Body/Allergies/AlergicComponent.cs | 12 ++++++++++++
.../Locale/ru-RU/ADT/traits/disabilities.ftl | 3 +++
Resources/Prototypes/ADT/Traits/disabilities.yml | 15 +++++++++++++++
4 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/Content.Server/ADT/Body/AllergicSystem.cs b/Content.Server/ADT/Body/AllergicSystem.cs
index 33f5ff9791b..e447607c3f2 100644
--- a/Content.Server/ADT/Body/AllergicSystem.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.cs
@@ -56,7 +56,7 @@ private List> GetRandomAllergies(int min, int max)
public void OnInit(EntityUid uid, AllergicComponent component, ComponentInit args)
{
- component.Triggers = GetRandomAllergies(3, 5);
+ component.Triggers = GetRandomAllergies(component.Min, component.Max);
}
public void OnGetReagentEffects(EntityUid uid, AllergicComponent component, ref GetReagentEffectsEvent ev)
diff --git a/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs b/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
index a34fcaf3b9c..6ba6dde4e0c 100644
--- a/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
+++ b/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
@@ -9,4 +9,16 @@ public sealed partial class AllergicComponent : Component
{
[DataField]
public List> Triggers = new();
+
+ ///
+ /// Минимальное количество назначенных аллергенов при инициализации компонента.
+ ///
+ [DataField(readOnly: true)]
+ public int Min = 1;
+
+ ///
+ /// Максимальное количество назначенных аллергенов при инициализации компонента.
+ ///
+ [DataField(readOnly: true)]
+ public int Max = 3;
}
diff --git a/Resources/Locale/ru-RU/ADT/traits/disabilities.ftl b/Resources/Locale/ru-RU/ADT/traits/disabilities.ftl
index 70199c66512..1d4740071f3 100644
--- a/Resources/Locale/ru-RU/ADT/traits/disabilities.ftl
+++ b/Resources/Locale/ru-RU/ADT/traits/disabilities.ftl
@@ -18,3 +18,6 @@ trait-unadapted-to-space-desc = Из-за особого строения ваш
trait-touchy-name = Зрительная агнозия
trait-touchy-desc = Вы определяете вещи и их внешний вид на ощупь. (examine работает только с теми предметами которые в пределах вашей досягаемости)
+
+trait-allergic-name = Аллергик
+trait-allergic-desc = У вас есть аллергия.
diff --git a/Resources/Prototypes/ADT/Traits/disabilities.yml b/Resources/Prototypes/ADT/Traits/disabilities.yml
index 2fb770f1f9f..993e15a16c8 100644
--- a/Resources/Prototypes/ADT/Traits/disabilities.yml
+++ b/Resources/Prototypes/ADT/Traits/disabilities.yml
@@ -82,3 +82,18 @@
quirk: true
speciesBlacklist:
- IPC
+
+- type: trait
+ id: Allergic
+ name: trait-allergic-name
+ description: trait-allergic-desc
+ category: Quirks
+ component:
+ - type: Allergic
+ cost: -1
+ quirk: true
+ speciesBlacklist:
+ - NovakidSpecies
+ - Diona
+ - SlimePerson
+ - IPC
\ No newline at end of file
From 5c720d4ed9f435a3eedf0e61518d4f7c5898b9ae Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Mon, 2 Mar 2026 19:42:08 +0300
Subject: [PATCH 03/11] [TWEAK] YAML fix
---
Resources/Prototypes/ADT/Traits/disabilities.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Resources/Prototypes/ADT/Traits/disabilities.yml b/Resources/Prototypes/ADT/Traits/disabilities.yml
index 993e15a16c8..483a3a6734e 100644
--- a/Resources/Prototypes/ADT/Traits/disabilities.yml
+++ b/Resources/Prototypes/ADT/Traits/disabilities.yml
@@ -88,7 +88,7 @@
name: trait-allergic-name
description: trait-allergic-desc
category: Quirks
- component:
+ components:
- type: Allergic
cost: -1
quirk: true
From 09b25fcf0a1d595fa3d3ec8f1c9b7dd56bb5a25b Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Mon, 2 Mar 2026 19:47:16 +0300
Subject: [PATCH 04/11] [TWEAK] Swab popups
---
.../Botany/Systems/BotanySwabSystem.cs | 36 +++++++++++--------
1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs
index c68e8e439c6..c1a9c39c5ed 100644
--- a/Content.Server/Botany/Systems/BotanySwabSystem.cs
+++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs
@@ -54,10 +54,28 @@ private void OnExamined(EntityUid uid, BotanySwabComponent swab, ExaminedEvent a
///
private void OnAfterInteract(EntityUid uid, BotanySwabComponent swab, AfterInteractEvent args)
{
- if (args.Target == null || !args.CanReach ||
- !HasComp(args.Target) && !HasComp(args.Target)) // ADT-Tweak
+ // ADT-Tweak-Start
+ bool isTargetPlant = HasComp(args.Target);
+ bool isTargetBody = HasComp(args.Target);
+ // ADT-Tweak-End
+
+ if (args.Target == null || !args.CanReach || !isTargetPlant && !isTargetBody) // ADT-Tweak
return;
+ // ADT-Tweak-Start
+ if (isTargetPlant && swab.AllergicTriggers != null)
+ {
+ _popupSystem.PopupEntity(Loc.GetString("botany-swab-unusable-plant"), args.User, args.User);
+ return;
+ }
+
+ if (isTargetBody && swab.SeedData != null)
+ {
+ _popupSystem.PopupEntity(Loc.GetString("botany-swab-unusable-bio"), args.User, args.User);
+ return;
+ }
+ // ADT-Tweak-End
+
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, swab.SwabDelay, new BotanySwabDoAfterEvent(), uid, target: args.Target, used: uid)
{
Broadcast = true,
@@ -72,12 +90,6 @@ private void OnAfterInteract(EntityUid uid, BotanySwabComponent swab, AfterInter
///
private void OnPlantDoAfter(EntityUid uid, BotanySwabComponent swab, PlantHolderComponent plant, DoAfterEvent args)
{
- if (swab.AllergicTriggers != null)
- {
- _popupSystem.PopupEntity(Loc.GetString("botany-swab-unusable-plant"), args.Args.Target!.Value, args.Args.User);
- return;
- }
-
if (swab.SeedData == null)
{
// Pick up pollen
@@ -102,12 +114,6 @@ private void OnPlantDoAfter(EntityUid uid, BotanySwabComponent swab, PlantHolder
///
private void OnCreatureDoAfter(EntityUid uid, BotanySwabComponent swab, DoAfterEvent args)
{
- if (swab.SeedData != null)
- {
- _popupSystem.PopupEntity(Loc.GetString("botany-swab-unusable-bio"), args.Args.Target!.Value, args.Args.User);
- return;
- }
-
if (TryComp(args.Args.Target!.Value, out var allergic))
if (swab.AllergicTriggers == null)
swab.AllergicTriggers = allergic.Triggers;
@@ -119,7 +125,7 @@ private void OnCreatureDoAfter(EntityUid uid, BotanySwabComponent swab, DoAfterE
if (swab.AllergicTriggers == null)
swab.AllergicTriggers = new();
- _popupSystem.PopupEntity(Loc.GetString("botany-swab-used-bio"), args.Args.Target!.Value, args.Args.User);
+ _popupSystem.PopupEntity(Loc.GetString("botany-swab-used-bio"), args.Args.User, args.Args.User);
}
// ADT-Tweak-End
From 8d95223403d8daa1ad500e7cf858b4791c0fe673 Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Mon, 2 Mar 2026 19:55:40 +0300
Subject: [PATCH 05/11] [FIX] Clanker driven development
---
Content.Server/ADT/Body/AllergicSystem.cs | 14 ++++++++++----
.../EntitySystems/DiseaseDiagnoserSystem.cs | 3 +++
.../Botany/Components/BotanySwabComponent.cs | 4 ++--
Content.Server/Botany/Systems/BotanySwabSystem.cs | 2 +-
Resources/Locale/ru-RU/ADT/allergies/allergies.ftl | 4 ++--
.../Locale/ru-RU/ADT/reagents/meta/medicine.ftl | 2 +-
6 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/Content.Server/ADT/Body/AllergicSystem.cs b/Content.Server/ADT/Body/AllergicSystem.cs
index e447607c3f2..3395f78e351 100644
--- a/Content.Server/ADT/Body/AllergicSystem.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.cs
@@ -32,16 +32,22 @@ public override void Initialize()
private List> GetRandomAllergies(int min, int max)
{
- var reagentsCount = _proto.Count();
+ int reagentsCount = _proto.Count();
if (reagentsCount == 0)
return new();
- int allergiesCount = _random.Next(min, max);
+ int safeMin = Math.Clamp(min, 0, reagentsCount);
+ int safeMax = Math.Clamp(max, safeMin, reagentsCount);
+ int allergiesCount = _random.Next(safeMin, safeMax + 1);
- List picked = Enumerable.Range(0, allergiesCount).Select(_ => _random.Next(reagentsCount)).ToList();
- var index = 0;
+ var picked = new HashSet();
+ while (picked.Count < allergiesCount)
+ {
+ picked.Add(_random.Next(reagentsCount));
+ }
+ int index = 0;
List> allergies = new();
foreach (var proto in _proto.EnumeratePrototypes())
diff --git a/Content.Server/ADT/Medical/EntitySystems/DiseaseDiagnoserSystem.cs b/Content.Server/ADT/Medical/EntitySystems/DiseaseDiagnoserSystem.cs
index 4a67f8b8173..b0f65efc5c3 100644
--- a/Content.Server/ADT/Medical/EntitySystems/DiseaseDiagnoserSystem.cs
+++ b/Content.Server/ADT/Medical/EntitySystems/DiseaseDiagnoserSystem.cs
@@ -131,6 +131,9 @@ public void Finish(Entity entity)
|| !_container.TryGetContainer(uid, comp.ContainerId, out var container))
return;
+ if (container.ContainedEntities.Count == 0)
+ return;
+
var swabEnt = container.ContainedEntities.First();
if (!TryComp(swabEnt, out var swabComponent))
return;
diff --git a/Content.Server/Botany/Components/BotanySwabComponent.cs b/Content.Server/Botany/Components/BotanySwabComponent.cs
index 2e81a1cd38b..cbd5bd07d14 100644
--- a/Content.Server/Botany/Components/BotanySwabComponent.cs
+++ b/Content.Server/Botany/Components/BotanySwabComponent.cs
@@ -17,12 +17,12 @@ public sealed partial class BotanySwabComponent : Component
///
public SeedData? SeedData;
- /// ADT-Tweak-Start
///
/// Allergic triggers from players swabbed.
///
- /// ADT-Tweak-End
+ // ADT-Tweak-Start
[DataField]
public List>? AllergicTriggers;
+ // ADT-Tweak-End
}
}
diff --git a/Content.Server/Botany/Systems/BotanySwabSystem.cs b/Content.Server/Botany/Systems/BotanySwabSystem.cs
index c1a9c39c5ed..abcf09db9e1 100644
--- a/Content.Server/Botany/Systems/BotanySwabSystem.cs
+++ b/Content.Server/Botany/Systems/BotanySwabSystem.cs
@@ -116,7 +116,7 @@ private void OnCreatureDoAfter(EntityUid uid, BotanySwabComponent swab, DoAfterE
{
if (TryComp(args.Args.Target!.Value, out var allergic))
if (swab.AllergicTriggers == null)
- swab.AllergicTriggers = allergic.Triggers;
+ swab.AllergicTriggers = allergic.Triggers.ToList();
else
/** Snowball unique allergies on swab */
swab.AllergicTriggers = swab.AllergicTriggers.Concat(allergic.Triggers).Distinct().ToList();
diff --git a/Resources/Locale/ru-RU/ADT/allergies/allergies.ftl b/Resources/Locale/ru-RU/ADT/allergies/allergies.ftl
index 71a78ccb8f4..e2212c312e2 100644
--- a/Resources/Locale/ru-RU/ADT/allergies/allergies.ftl
+++ b/Resources/Locale/ru-RU/ADT/allergies/allergies.ftl
@@ -1,7 +1,7 @@
health-analyzer-window-entity-allergy-text = Имеется аллергия
-botany-swab-unusable-plant = Палочка непригода для сбора пыльцы.
-botany-swab-unusable-bio = Палочка непригода для взятия биологических образов.
+botany-swab-unusable-plant = Палочка непригодна для сбора пыльцы.
+botany-swab-unusable-bio = Палочка непригодна для взятия биологических образцов.
botany-swab-used-bio = Вы собрали биологические образцы.
diff --git a/Resources/Locale/ru-RU/ADT/reagents/meta/medicine.ftl b/Resources/Locale/ru-RU/ADT/reagents/meta/medicine.ftl
index 13efb5d541e..79a7a81e6bc 100644
--- a/Resources/Locale/ru-RU/ADT/reagents/meta/medicine.ftl
+++ b/Resources/Locale/ru-RU/ADT/reagents/meta/medicine.ftl
@@ -56,4 +56,4 @@ reagent-name-devazigon = Девазигон
reagent-desc-devazigon = Антитоксин выведенный на основе нейротоксина, используется в устранении последствий сильного отравления.
reagent-name-sanallergium = Саналлергин
-reagent-desc-sanallergium = Используется для перманентного снижения имунной реакции пациента к аллергенам в крови.
+reagent-desc-sanallergium = Используется для перманентного снижения иммунной реакции пациента к аллергенам в крови.
From bcb05c5764507292c533c2159122f45ed41f7fc1 Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Tue, 3 Mar 2026 22:50:34 +0300
Subject: [PATCH 06/11] [ADD] Allergic stack and reactions
---
Content.Server/ADT/Body/AllergicEvents.cs | 22 ++++++
.../ADT/Body/AllergicSystem.Shock.cs | 68 +++++++++++++++++++
.../ADT/Body/AllergicSystem.Stacks.cs | 63 +++++++++++++++++
Content.Server/ADT/Body/AllergicSystem.cs | 60 +++++++++++-----
.../ADT/Body/Allergies/AlergicComponent.cs | 30 ++++++++
.../Prototypes/AllergicReactionPrototype.cs | 17 +++++
Resources/Prototypes/ADT/Medical/allergy.yml | 6 ++
7 files changed, 250 insertions(+), 16 deletions(-)
create mode 100644 Content.Server/ADT/Body/AllergicEvents.cs
create mode 100644 Content.Server/ADT/Body/AllergicSystem.Shock.cs
create mode 100644 Content.Server/ADT/Body/AllergicSystem.Stacks.cs
create mode 100644 Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs
create mode 100644 Resources/Prototypes/ADT/Medical/allergy.yml
diff --git a/Content.Server/ADT/Body/AllergicEvents.cs b/Content.Server/ADT/Body/AllergicEvents.cs
new file mode 100644
index 00000000000..8a35aa821a6
--- /dev/null
+++ b/Content.Server/ADT/Body/AllergicEvents.cs
@@ -0,0 +1,22 @@
+using Content.Shared.Chemistry.Reagent;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.ADT.Body;
+
+///
+/// When allergy triggered by allergens in bloodstream.
+///
+[ByRefEvent]
+public struct AllergyTriggeredEvent
+{
+ ///
+ /// Allergen.
+ ///
+ public ProtoId Reagent;
+}
+
+///
+/// When allergy stack has faded to zero.
+///
+[ByRefEvent]
+public struct AllergyFadedEvent;
\ No newline at end of file
diff --git a/Content.Server/ADT/Body/AllergicSystem.Shock.cs b/Content.Server/ADT/Body/AllergicSystem.Shock.cs
new file mode 100644
index 00000000000..5523b282537
--- /dev/null
+++ b/Content.Server/ADT/Body/AllergicSystem.Shock.cs
@@ -0,0 +1,68 @@
+using System.Linq;
+using Content.Shared.ADT.Body.Allergies;
+using Content.Shared.ADT.Body.Allergies.Prototypes;
+using Content.Shared.EntityEffects;
+using Robust.Shared.Timing;
+
+namespace Content.Server.ADT.Body;
+
+public sealed partial class AllergicSystem : EntitySystem
+{
+ private List _reactions = new();
+
+ [Dependency] private IGameTiming _timing = default!;
+ [Dependency] private SharedEntityEffectsSystem _effectSystem = default!;
+
+ public void InitializeShock()
+ {
+ SubscribeLocalEvent(Unshock);
+ _reactions = _proto.EnumeratePrototypes().ToList();
+ }
+
+ private void ShockOnTrigger(EntityUid uid, AllergicComponent allergic, ref AllergyTriggeredEvent ev)
+ {
+ if (!allergic.InShock)
+ allergic.InShock = true;
+ }
+
+ private void Unshock(EntityUid uid, AllergicComponent allergic, ref AllergyFadedEvent ev)
+ {
+ allergic.InShock = false;
+ }
+
+ private void AssignNextShockTiming(AllergicComponent allergic)
+ {
+ allergic.NextShockEvent = _timing.CurTime + TimeSpan.FromMilliseconds(_random.NextFloat(2000, 4000));
+ }
+
+ private void UpdateShock(EntityUid uid, AllergicComponent allergic)
+ {
+ if (!allergic.InShock)
+ return;
+
+ if (allergic.NextShockEvent == TimeSpan.Zero)
+ {
+ AssignNextShockTiming(allergic);
+ return;
+ }
+
+ if (_timing.CurTime > allergic.NextShockEvent)
+ return;
+
+ AllergicReactionPrototype? picked = null;
+
+ foreach (var reaction in _reactions)
+ {
+ if (allergic.AllergyStack >= reaction.StackThreshold)
+ picked = reaction;
+ }
+
+ if (picked != null)
+ {
+ EntityEffect effect = picked.Effects[_random.Next(0, picked.Effects.Count())];
+ _effectSystem.ApplyEffect(uid, effect);
+ }
+
+ AssignNextShockTiming(allergic);
+ }
+}
\ No newline at end of file
diff --git a/Content.Server/ADT/Body/AllergicSystem.Stacks.cs b/Content.Server/ADT/Body/AllergicSystem.Stacks.cs
new file mode 100644
index 00000000000..3da68084db9
--- /dev/null
+++ b/Content.Server/ADT/Body/AllergicSystem.Stacks.cs
@@ -0,0 +1,63 @@
+using System.Linq;
+using Content.Shared.ADT.Body.Allergies;
+using Content.Shared.Chemistry.Reagent;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.ADT.Body;
+
+public sealed partial class AllergicSystem : EntitySystem
+{
+ #region Events
+
+ private void IncrementStackOnTrigger(EntityUid uid, AllergicComponent allergic, ref AllergyTriggeredEvent ev)
+ {
+ Log.Debug("Adjusting stack");
+ AdjustAllergyStack(uid, allergic.StackGrow, allergic);
+ }
+
+ #endregion
+
+ #region Flow
+
+ private void UpdateStack(EntityUid uid, AllergicComponent allergic)
+ {
+ if (allergic.AllergyStack <= 0)
+ return;
+
+ if (allergic.NextStackFade > _timing.CurTime)
+ return;
+
+ float newStack = allergic.AllergyStack + allergic.StackFade;
+ SetAllergyStack(uid, newStack, allergic);
+
+ if (newStack <= 0)
+ {
+ var faded = new AllergyFadedEvent();
+ RaiseLocalEvent(uid, ref faded);
+ }
+
+ allergic.NextStackFade = allergic.NextStackFade + allergic.StackFadeRate;
+ }
+
+ #endregion
+
+ #region API
+
+ public void AdjustAllergyStack(EntityUid uid, float relativeStack, AllergicComponent? allergic = null)
+ {
+ if (!Resolve(uid, ref allergic))
+ return;
+
+ SetAllergyStack(uid, allergic.AllergyStack + relativeStack, allergic);
+ }
+
+ public void SetAllergyStack(EntityUid uid, float stack, AllergicComponent? allergic = null)
+ {
+ if (!Resolve(uid, ref allergic))
+ return;
+
+ allergic.AllergyStack = MathF.Min(MathF.Max(0, stack), allergic.MaximumStack);
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/Content.Server/ADT/Body/AllergicSystem.cs b/Content.Server/ADT/Body/AllergicSystem.cs
index 3395f78e351..6a84b864851 100644
--- a/Content.Server/ADT/Body/AllergicSystem.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.cs
@@ -7,17 +7,16 @@
using Content.Shared.EntityEffects.Effects;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
+using Robust.Shared.Timing;
namespace Content.Server.ADT.Body;
-public sealed class AllergicSystem : EntitySystem
+public sealed partial class AllergicSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
-
[Dependency] private readonly IPrototypeManager _proto = default!;
private ProtoId _allergyDamageGroup = "Poison";
-
private DamageTypePrototype? _allergyDamageTypeProto;
public override void Initialize()
@@ -27,9 +26,44 @@ public override void Initialize()
SubscribeLocalEvent(OnInit);
SubscribeLocalEvent(OnGetReagentEffects);
+ SubscribeLocalEvent(OnAllergyTriggered);
+
+ InitializeShock();
+
_allergyDamageTypeProto = _proto.Index(_allergyDamageGroup);
}
+ public void OnInit(EntityUid uid, AllergicComponent component, ComponentInit args)
+ {
+ component.Triggers = GetRandomAllergies(component.Min, component.Max);
+ }
+
+ public void OnGetReagentEffects(EntityUid uid, AllergicComponent component, ref GetReagentEffectsEvent ev)
+ {
+ if (!component.Triggers.Contains(ev.Reagent.Prototype))
+ return;
+
+ var damageSpecifier = new DamageSpecifier(_allergyDamageTypeProto!, 0.25);
+ var damageEffect = new HealthChange
+ {
+ Damage = damageSpecifier
+ };
+
+ AllergyTriggeredEvent triggered = new();
+ RaiseLocalEvent(uid, ref triggered);
+
+ Log.Debug("We got some nasty shit inside!");
+
+ ev.Effects = ev.Effects.Append(damageEffect).ToArray();
+ }
+
+ private void OnAllergyTriggered(EntityUid uid, AllergicComponent allergic, ref AllergyTriggeredEvent ev)
+ {
+ Log.Debug("We got trigger event");
+ ShockOnTrigger(uid, allergic, ref ev);
+ IncrementStackOnTrigger(uid, allergic, ref ev);
+ }
+
private List> GetRandomAllergies(int min, int max)
{
int reagentsCount = _proto.Count();
@@ -60,22 +94,16 @@ private List> GetRandomAllergies(int min, int max)
return allergies;
}
- public void OnInit(EntityUid uid, AllergicComponent component, ComponentInit args)
+ public override void Update(float frameTime)
{
- component.Triggers = GetRandomAllergies(component.Min, component.Max);
- }
+ base.Update(frameTime);
- public void OnGetReagentEffects(EntityUid uid, AllergicComponent component, ref GetReagentEffectsEvent ev)
- {
- if (!component.Triggers.Contains(ev.Reagent.Prototype))
- return;
+ var query = EntityQueryEnumerator();
- var damageSpecifier = new DamageSpecifier(_allergyDamageTypeProto!, 0.5);
- var damageEffect = new HealthChange
+ while (query.MoveNext(out var uid, out var allergic))
{
- Damage = damageSpecifier
- };
-
- ev.Effects = ev.Effects.Append(damageEffect).ToArray();
+ UpdateStack(uid, allergic);
+ UpdateShock(uid, allergic);
+ }
}
}
diff --git a/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs b/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
index 6ba6dde4e0c..107cd2bc845 100644
--- a/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
+++ b/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
@@ -10,6 +10,36 @@ public sealed partial class AllergicComponent : Component
[DataField]
public List> Triggers = new();
+ [DataField]
+ public bool InShock = false;
+
+ [ViewVariables(VVAccess.ReadOnly)]
+ [DataField]
+ public TimeSpan NextShockEvent = TimeSpan.Zero;
+
+ [DataField]
+ public float AllergyStack = 0f;
+
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
+ public float MaximumStack = 30f;
+
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
+ public float StackFade = -0.2f;
+
+ [ViewVariables(VVAccess.ReadOnly)]
+ [DataField]
+ public TimeSpan NextStackFade = TimeSpan.Zero;
+
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
+ public TimeSpan StackFadeRate = TimeSpan.FromSeconds(5);
+
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
+ public float StackGrow = 0.5f;
+
///
/// Минимальное количество назначенных аллергенов при инициализации компонента.
///
diff --git a/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs b/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs
new file mode 100644
index 00000000000..44b400ad3bc
--- /dev/null
+++ b/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs
@@ -0,0 +1,17 @@
+using Content.Shared.EntityEffects;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.ADT.Body.Allergies.Prototypes;
+
+[Prototype("allergicReaction")]
+public sealed partial class AllergicReactionPrototype : IPrototype
+{
+ [IdDataField]
+ public string ID { get; private set; } = default!;
+
+ [DataField("effects")]
+ public EntityEffect[] Effects = default!;
+
+ [DataField]
+ public float StackThreshold;
+}
diff --git a/Resources/Prototypes/ADT/Medical/allergy.yml b/Resources/Prototypes/ADT/Medical/allergy.yml
new file mode 100644
index 00000000000..44211b3eddf
--- /dev/null
+++ b/Resources/Prototypes/ADT/Medical/allergy.yml
@@ -0,0 +1,6 @@
+- type: allergicReaction
+ id: mildAllergicReaction
+ threshold: 5
+ effects:
+ - !type:Jitter
+ time: 5
\ No newline at end of file
From 2d8fc6e44ec050379c8181d37de2da1b29e2c201 Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Wed, 4 Mar 2026 01:05:12 +0300
Subject: [PATCH 07/11] [FIX] Fixes and etc etc...
---
Content.Server/ADT/Body/AllergicEvents.cs | 8 +---
.../ADT/Body/AllergicSystem.Shock.cs | 14 ++----
.../ADT/Body/AllergicSystem.Stacks.cs | 1 -
Content.Server/ADT/Body/AllergicSystem.cs | 8 +---
...ergicComponent.cs => AllergicComponent.cs} | 43 ++++++++++++++++---
.../Prototypes/AllergicReactionPrototype.cs | 2 +-
6 files changed, 45 insertions(+), 31 deletions(-)
rename Content.Shared/ADT/Body/Allergies/{AlergicComponent.cs => AllergicComponent.cs} (53%)
diff --git a/Content.Server/ADT/Body/AllergicEvents.cs b/Content.Server/ADT/Body/AllergicEvents.cs
index 8a35aa821a6..26b9b9ed947 100644
--- a/Content.Server/ADT/Body/AllergicEvents.cs
+++ b/Content.Server/ADT/Body/AllergicEvents.cs
@@ -7,13 +7,7 @@ namespace Content.Server.ADT.Body;
/// When allergy triggered by allergens in bloodstream.
///
[ByRefEvent]
-public struct AllergyTriggeredEvent
-{
- ///
- /// Allergen.
- ///
- public ProtoId Reagent;
-}
+public struct AllergyTriggeredEvent;
///
/// When allergy stack has faded to zero.
diff --git a/Content.Server/ADT/Body/AllergicSystem.Shock.cs b/Content.Server/ADT/Body/AllergicSystem.Shock.cs
index 5523b282537..15257ccb952 100644
--- a/Content.Server/ADT/Body/AllergicSystem.Shock.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.Shock.cs
@@ -19,25 +19,19 @@ public void InitializeShock()
_reactions = _proto.EnumeratePrototypes().ToList();
}
- private void ShockOnTrigger(EntityUid uid, AllergicComponent allergic, ref AllergyTriggeredEvent ev)
- {
- if (!allergic.InShock)
- allergic.InShock = true;
- }
-
private void Unshock(EntityUid uid, AllergicComponent allergic, ref AllergyFadedEvent ev)
{
- allergic.InShock = false;
+ allergic.NextShockEvent = TimeSpan.Zero;
}
private void AssignNextShockTiming(AllergicComponent allergic)
{
- allergic.NextShockEvent = _timing.CurTime + TimeSpan.FromMilliseconds(_random.NextFloat(2000, 4000));
+ allergic.NextShockEvent = _timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(allergic.MinimumTimeTilNextShockEvent, allergic.MaximumTimeTilNextShockEvent));
}
private void UpdateShock(EntityUid uid, AllergicComponent allergic)
{
- if (!allergic.InShock)
+ if (allergic.AllergyStack <= 0)
return;
if (allergic.NextShockEvent == TimeSpan.Zero)
@@ -46,7 +40,7 @@ private void UpdateShock(EntityUid uid, AllergicComponent allergic)
return;
}
- if (_timing.CurTime > allergic.NextShockEvent)
+ if (allergic.NextShockEvent > _timing.CurTime)
return;
AllergicReactionPrototype? picked = null;
diff --git a/Content.Server/ADT/Body/AllergicSystem.Stacks.cs b/Content.Server/ADT/Body/AllergicSystem.Stacks.cs
index 3da68084db9..c5a212e86e9 100644
--- a/Content.Server/ADT/Body/AllergicSystem.Stacks.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.Stacks.cs
@@ -11,7 +11,6 @@ public sealed partial class AllergicSystem : EntitySystem
private void IncrementStackOnTrigger(EntityUid uid, AllergicComponent allergic, ref AllergyTriggeredEvent ev)
{
- Log.Debug("Adjusting stack");
AdjustAllergyStack(uid, allergic.StackGrow, allergic);
}
diff --git a/Content.Server/ADT/Body/AllergicSystem.cs b/Content.Server/ADT/Body/AllergicSystem.cs
index 6a84b864851..6ea0e821269 100644
--- a/Content.Server/ADT/Body/AllergicSystem.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.cs
@@ -16,7 +16,7 @@ public sealed partial class AllergicSystem : EntitySystem
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
- private ProtoId _allergyDamageGroup = "Poison";
+ private ProtoId _allergyDamageType = "Poison";
private DamageTypePrototype? _allergyDamageTypeProto;
public override void Initialize()
@@ -30,7 +30,7 @@ public override void Initialize()
InitializeShock();
- _allergyDamageTypeProto = _proto.Index(_allergyDamageGroup);
+ _allergyDamageTypeProto = _proto.Index(_allergyDamageType);
}
public void OnInit(EntityUid uid, AllergicComponent component, ComponentInit args)
@@ -52,15 +52,11 @@ public void OnGetReagentEffects(EntityUid uid, AllergicComponent component, ref
AllergyTriggeredEvent triggered = new();
RaiseLocalEvent(uid, ref triggered);
- Log.Debug("We got some nasty shit inside!");
-
ev.Effects = ev.Effects.Append(damageEffect).ToArray();
}
private void OnAllergyTriggered(EntityUid uid, AllergicComponent allergic, ref AllergyTriggeredEvent ev)
{
- Log.Debug("We got trigger event");
- ShockOnTrigger(uid, allergic, ref ev);
IncrementStackOnTrigger(uid, allergic, ref ev);
}
diff --git a/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs b/Content.Shared/ADT/Body/Allergies/AllergicComponent.cs
similarity index 53%
rename from Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
rename to Content.Shared/ADT/Body/Allergies/AllergicComponent.cs
index 107cd2bc845..13f19bc1c70 100644
--- a/Content.Shared/ADT/Body/Allergies/AlergicComponent.cs
+++ b/Content.Shared/ADT/Body/Allergies/AllergicComponent.cs
@@ -10,31 +10,62 @@ public sealed partial class AllergicComponent : Component
[DataField]
public List> Triggers = new();
- [DataField]
- public bool InShock = false;
-
+ ///
+ /// Время следующего события анафилактического шока.
+ ///
[ViewVariables(VVAccess.ReadOnly)]
[DataField]
public TimeSpan NextShockEvent = TimeSpan.Zero;
+ ///
+ /// Минимальное время в секундах до следующего события анафилактического шока.
+ ///
+ [DataField]
+ public float MinimumTimeTilNextShockEvent = 5f;
+
+ ///
+ /// Максимальное время в секундах до следующего события анафилактического шока.
+ ///
+ [DataField]
+ public float MaximumTimeTilNextShockEvent = 10f;
+
+ ///
+ /// Стак аллергии.
+ ///
[DataField]
public float AllergyStack = 0f;
+ ///
+ /// Максимальный размер стака аллергии.
+ ///
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float MaximumStack = 30f;
+ ///
+ /// Значение затухания (декремента) стака.
+ ///
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float StackFade = -0.2f;
+ ///
+ /// Скорость затухания (декремента) стака.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
+ public TimeSpan StackFadeRate = TimeSpan.FromSeconds(5);
+
+ ///
+ /// Время следующего декремента стака.
+ ///
[ViewVariables(VVAccess.ReadOnly)]
[DataField]
public TimeSpan NextStackFade = TimeSpan.Zero;
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField]
- public TimeSpan StackFadeRate = TimeSpan.FromSeconds(5);
+ ///
+ /// Значение роста стака при метаболизме аллергена.
+ ///
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
diff --git a/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs b/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs
index 44b400ad3bc..d995f27af18 100644
--- a/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs
+++ b/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs
@@ -12,6 +12,6 @@ public sealed partial class AllergicReactionPrototype : IPrototype
[DataField("effects")]
public EntityEffect[] Effects = default!;
- [DataField]
+ [DataField("threshold", required: true)]
public float StackThreshold;
}
From c3a98017d9e027a391a8869b5d1a8736aadda170 Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Sat, 14 Mar 2026 06:03:46 +0300
Subject: [PATCH 08/11] =?UTF-8?q?[TWEAK]=20=D0=A1=D0=BD=D1=8F=D1=82=D0=B8?=
=?UTF-8?q?=D0=B5=20=D1=81=D0=B8=D0=BC=D0=BF=D1=82=D0=BE=D0=BC=D0=BE=D0=B2?=
=?UTF-8?q?=20=D0=B0=D0=BB=D0=BB=D0=B5=D1=80=D0=B3=D0=B8=D0=B8,=20=D0=B1?=
=?UTF-8?q?=D0=B0=D0=BB=D0=B0=D0=BD=D1=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ADT/Body/AllergicSystem.Shock.cs | 8 +--
.../AdjustAllergicStackEffectSystem.cs | 19 +++++++
.../ADT/Body/Allergies/AllergicComponent.cs | 2 +-
.../Prototypes/AllergicReactionPrototype.cs | 6 +++
.../ADT/EntityEffects/AdjustAllergicStack.cs | 24 +++++++++
.../ru-RU/ADT/guidebook/chemistry/effects.ftl | 17 ++++++-
Resources/Prototypes/ADT/Medical/allergy.yml | 51 ++++++++++++++++++-
Resources/Prototypes/Reagents/medicine.yml | 2 +
8 files changed, 123 insertions(+), 6 deletions(-)
create mode 100644 Content.Server/ADT/EntityEffects/AdjustAllergicStackEffectSystem.cs
create mode 100644 Content.Shared/ADT/EntityEffects/AdjustAllergicStack.cs
diff --git a/Content.Server/ADT/Body/AllergicSystem.Shock.cs b/Content.Server/ADT/Body/AllergicSystem.Shock.cs
index 15257ccb952..c7fd213edc9 100644
--- a/Content.Server/ADT/Body/AllergicSystem.Shock.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.Shock.cs
@@ -51,12 +51,14 @@ private void UpdateShock(EntityUid uid, AllergicComponent allergic)
picked = reaction;
}
- if (picked != null)
+ if (picked == null)
+ return;
+
+ foreach (var effect in picked.Effects)
{
- EntityEffect effect = picked.Effects[_random.Next(0, picked.Effects.Count())];
_effectSystem.ApplyEffect(uid, effect);
}
AssignNextShockTiming(allergic);
}
-}
\ No newline at end of file
+}
diff --git a/Content.Server/ADT/EntityEffects/AdjustAllergicStackEffectSystem.cs b/Content.Server/ADT/EntityEffects/AdjustAllergicStackEffectSystem.cs
new file mode 100644
index 00000000000..47f858e70a8
--- /dev/null
+++ b/Content.Server/ADT/EntityEffects/AdjustAllergicStackEffectSystem.cs
@@ -0,0 +1,19 @@
+using Content.Server.ADT.Body;
+using Content.Shared.ADT.Body.Allergies;
+using Content.Shared.EntityEffects;
+using Content.Shared.EntityEffects.Effects;
+
+namespace Content.Server.ADT.EntityEffects;
+
+///
+/// Эффект для изменения значения стака аллергии.
+///
+public sealed partial class AdjustAllergicStackEntityEffectSystem : EntityEffectSystem
+{
+ [Dependency] private AllergicSystem _allergic = default!;
+
+ protected override void Effect(Entity entity, ref EntityEffectEvent args)
+ {
+ _allergic.AdjustAllergyStack(entity, args.Effect.Amount * args.Scale);
+ }
+}
diff --git a/Content.Shared/ADT/Body/Allergies/AllergicComponent.cs b/Content.Shared/ADT/Body/Allergies/AllergicComponent.cs
index 13f19bc1c70..0031f9e16c0 100644
--- a/Content.Shared/ADT/Body/Allergies/AllergicComponent.cs
+++ b/Content.Shared/ADT/Body/Allergies/AllergicComponent.cs
@@ -40,7 +40,7 @@ public sealed partial class AllergicComponent : Component
///
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
- public float MaximumStack = 30f;
+ public float MaximumStack = 50f;
///
/// Значение затухания (декремента) стака.
diff --git a/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs b/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs
index d995f27af18..8c3a6a40558 100644
--- a/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs
+++ b/Content.Shared/ADT/Body/Allergies/Prototypes/AllergicReactionPrototype.cs
@@ -3,6 +3,12 @@
namespace Content.Shared.ADT.Body.Allergies.Prototypes;
+///
+/// Прототип аллергической реакции.
+///
+/// Определяет, какие эффекты будут назначены при наступлении события аллергического шока,
+/// если стак аллергической реакции у энтити >= StackThreshold.
+///
[Prototype("allergicReaction")]
public sealed partial class AllergicReactionPrototype : IPrototype
{
diff --git a/Content.Shared/ADT/EntityEffects/AdjustAllergicStack.cs b/Content.Shared/ADT/EntityEffects/AdjustAllergicStack.cs
new file mode 100644
index 00000000000..d5f23a9a858
--- /dev/null
+++ b/Content.Shared/ADT/EntityEffects/AdjustAllergicStack.cs
@@ -0,0 +1,24 @@
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects;
+
+///
+public sealed partial class AdjustAllergicStack : EntityEffectBase
+{
+ ///
+ /// Amount we're adjusting stack by.
+ ///
+ [DataField]
+ public float Amount = 0f;
+
+ public override string? EntityEffectGuidebookText(
+ IPrototypeManager prototype,
+ IEntitySystemManager entSys)
+ {
+ return Loc.GetString(
+ "reagent-effect-guidebook-adjust-allergic-stack",
+ ("chance", Probability),
+ ("amount", MathF.Abs(Amount).ToString("0.00")),
+ ("positive", Amount <= 0));
+ }
+}
diff --git a/Resources/Locale/ru-RU/ADT/guidebook/chemistry/effects.ftl b/Resources/Locale/ru-RU/ADT/guidebook/chemistry/effects.ftl
index 52143823464..96b92454e9e 100644
--- a/Resources/Locale/ru-RU/ADT/guidebook/chemistry/effects.ftl
+++ b/Resources/Locale/ru-RU/ADT/guidebook/chemistry/effects.ftl
@@ -11,5 +11,20 @@ reagent-effect-guidebook-teleport =
reagent-effect-guidebook-purge-allergies =
{ $chance ->
[1] Лечит
- *[other] лечат
+ *[other] лечит
} аллергию
+reagent-effect-guidebook-adjust-allergic-stack =
+ { $chance ->
+ [1] { $positive ->
+ [true] Ослабляет
+ *[false] Ухудшает
+ }
+ *[other] { $positive ->
+ [true] ослабляет
+ *[false] ухудшает
+ }
+ } симптомы аллергии на {
+ $positive ->
+ [true] [color=green]{$amount}[/color]
+ *[false] [color=red]{$amount}[/color]
+ } ед.
diff --git a/Resources/Prototypes/ADT/Medical/allergy.yml b/Resources/Prototypes/ADT/Medical/allergy.yml
index 44211b3eddf..ace0035ff5d 100644
--- a/Resources/Prototypes/ADT/Medical/allergy.yml
+++ b/Resources/Prototypes/ADT/Medical/allergy.yml
@@ -3,4 +3,53 @@
threshold: 5
effects:
- !type:Jitter
- time: 5
\ No newline at end of file
+ time: 5
+ probability: 0.3
+ - !type:Emote
+ emote: Cough
+ showInChat: true
+ probability: 0.3
+
+- type: allergicReaction
+ id: midAllergicReaction
+ threshold: 15
+ effects:
+ - !type:Jitter
+ time: 5
+ - !type:HealthChange
+ damage:
+ groups:
+ Airloss: -3
+ probability: 0.5
+ - !type:Emote
+ emote: Cough
+ showInChat: true
+ probability: 0.7
+
+- type: allergicReaction
+ id: highAllergicReaction
+ threshold: 25
+ effects:
+ - !type:Jitter
+ time: 5
+ - !type:HealthChange
+ damage:
+ groups:
+ Airloss: -5
+ - !type:Emote
+ emote: Cough
+ showInChat: true
+
+- type: allergicReaction
+ id: itsOverAllergicReaction
+ threshold: 35
+ effects:
+ - !type:Jitter
+ time: 5
+ - !type:HealthChange
+ damage:
+ groups:
+ Airloss: -10
+ - !type:Emote
+ emote: Cough
+ showInChat: true
diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml
index 69330474600..af6bc897c99 100644
--- a/Resources/Prototypes/Reagents/medicine.yml
+++ b/Resources/Prototypes/Reagents/medicine.yml
@@ -97,6 +97,8 @@
damage:
types:
Poison: -3
+ - !type:AdjustAllergicStack
+ amount: -1
- type: reagent
id: Ethylredoxrazine
From a04c63904abdc3f7afb48cff0cefbab4d8c2fad6 Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Sat, 14 Mar 2026 06:05:15 +0300
Subject: [PATCH 09/11] [FIX] Allergic reactions sort
---
Content.Server/ADT/Body/AllergicSystem.Shock.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Content.Server/ADT/Body/AllergicSystem.Shock.cs b/Content.Server/ADT/Body/AllergicSystem.Shock.cs
index c7fd213edc9..a0cb7c3d71c 100644
--- a/Content.Server/ADT/Body/AllergicSystem.Shock.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.Shock.cs
@@ -16,7 +16,9 @@ public sealed partial class AllergicSystem : EntitySystem
public void InitializeShock()
{
SubscribeLocalEvent(Unshock);
- _reactions = _proto.EnumeratePrototypes().ToList();
+ _reactions = _proto.EnumeratePrototypes()
+ .OrderBy(r => r.StackThreshold)
+ .ToList();
}
private void Unshock(EntityUid uid, AllergicComponent allergic, ref AllergyFadedEvent ev)
From 0d9654ad12c486b53f35d679e209c971cc9dae1a Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Sat, 14 Mar 2026 06:25:22 +0300
Subject: [PATCH 10/11] =?UTF-8?q?[TWEAK]=20=D0=94=D0=BE=D0=BA=D1=83=D0=BC?=
=?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8F,=20clanker-driven?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ADT/Body/AllergicSystem.Shock.cs | 2 +-
.../ADT/Body/AllergicSystem.Stacks.cs | 15 +++++++++++---
Content.Server/ADT/Body/AllergicSystem.cs | 20 +++++++++++++++----
.../ADT/Body/Allergies/AllergicComponent.cs | 4 +++-
4 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/Content.Server/ADT/Body/AllergicSystem.Shock.cs b/Content.Server/ADT/Body/AllergicSystem.Shock.cs
index a0cb7c3d71c..22db302039c 100644
--- a/Content.Server/ADT/Body/AllergicSystem.Shock.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.Shock.cs
@@ -1,8 +1,8 @@
-using System.Linq;
using Content.Shared.ADT.Body.Allergies;
using Content.Shared.ADT.Body.Allergies.Prototypes;
using Content.Shared.EntityEffects;
using Robust.Shared.Timing;
+using System.Linq;
namespace Content.Server.ADT.Body;
diff --git a/Content.Server/ADT/Body/AllergicSystem.Stacks.cs b/Content.Server/ADT/Body/AllergicSystem.Stacks.cs
index c5a212e86e9..1a35fad3431 100644
--- a/Content.Server/ADT/Body/AllergicSystem.Stacks.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.Stacks.cs
@@ -1,7 +1,4 @@
-using System.Linq;
using Content.Shared.ADT.Body.Allergies;
-using Content.Shared.Chemistry.Reagent;
-using Robust.Shared.Prototypes;
namespace Content.Server.ADT.Body;
@@ -42,6 +39,12 @@ private void UpdateStack(EntityUid uid, AllergicComponent allergic)
#region API
+ ///
+ /// Метод для изменения стака аллергии на относительное значение.
+ ///
+ ///
+ ///
+ ///
public void AdjustAllergyStack(EntityUid uid, float relativeStack, AllergicComponent? allergic = null)
{
if (!Resolve(uid, ref allergic))
@@ -50,6 +53,12 @@ public void AdjustAllergyStack(EntityUid uid, float relativeStack, AllergicCompo
SetAllergyStack(uid, allergic.AllergyStack + relativeStack, allergic);
}
+ ///
+ /// Метод для изменения стака аллергии.
+ ///
+ ///
+ ///
+ ///
public void SetAllergyStack(EntityUid uid, float stack, AllergicComponent? allergic = null)
{
if (!Resolve(uid, ref allergic))
diff --git a/Content.Server/ADT/Body/AllergicSystem.cs b/Content.Server/ADT/Body/AllergicSystem.cs
index 6ea0e821269..ce8c52880ad 100644
--- a/Content.Server/ADT/Body/AllergicSystem.cs
+++ b/Content.Server/ADT/Body/AllergicSystem.cs
@@ -1,4 +1,3 @@
-using System.Linq;
using Content.Server.Body.Systems;
using Content.Shared.ADT.Body.Allergies;
using Content.Shared.Chemistry.Reagent;
@@ -7,7 +6,7 @@
using Content.Shared.EntityEffects.Effects;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
-using Robust.Shared.Timing;
+using System.Linq;
namespace Content.Server.ADT.Body;
@@ -33,12 +32,19 @@ public override void Initialize()
_allergyDamageTypeProto = _proto.Index(_allergyDamageType);
}
- public void OnInit(EntityUid uid, AllergicComponent component, ComponentInit args)
+ private void OnInit(EntityUid uid, AllergicComponent component, ComponentInit args)
{
component.Triggers = GetRandomAllergies(component.Min, component.Max);
}
- public void OnGetReagentEffects(EntityUid uid, AllergicComponent component, ref GetReagentEffectsEvent ev)
+ ///
+ /// Метод для обработки метаболизма реагента.
+ /// При наличии аллергена, добавляет эффект изменения здоровья и вызывает AllergyTriggeredEvent.
+ ///
+ ///
+ ///
+ ///
+ private void OnGetReagentEffects(EntityUid uid, AllergicComponent component, ref GetReagentEffectsEvent ev)
{
if (!component.Triggers.Contains(ev.Reagent.Prototype))
return;
@@ -60,6 +66,12 @@ private void OnAllergyTriggered(EntityUid uid, AllergicComponent allergic, ref A
IncrementStackOnTrigger(uid, allergic, ref ev);
}
+ ///
+ /// Метод для получения рандомных аллергенов.
+ ///
+ /// Максимальное кол-во
+ /// Минимальное кол-во
+ ///
private List> GetRandomAllergies(int min, int max)
{
int reagentsCount = _proto.Count();
diff --git a/Content.Shared/ADT/Body/Allergies/AllergicComponent.cs b/Content.Shared/ADT/Body/Allergies/AllergicComponent.cs
index 0031f9e16c0..b4e55190ed9 100644
--- a/Content.Shared/ADT/Body/Allergies/AllergicComponent.cs
+++ b/Content.Shared/ADT/Body/Allergies/AllergicComponent.cs
@@ -7,6 +7,9 @@ namespace Content.Shared.ADT.Body.Allergies;
[RegisterComponent, NetworkedComponent]
public sealed partial class AllergicComponent : Component
{
+ ///
+ /// Список реагентов-аллергенов.
+ ///
[DataField]
public List> Triggers = new();
@@ -66,7 +69,6 @@ public sealed partial class AllergicComponent : Component
///
/// Значение роста стака при метаболизме аллергена.
///
-
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public float StackGrow = 0.5f;
From b6a6edc10a07b689d4760cc983ef26d7eae71743 Mon Sep 17 00:00:00 2001
From: Rorkh <78957156+Rorkh@users.noreply.github.com>
Date: Thu, 19 Mar 2026 13:57:30 +0300
Subject: [PATCH 11/11] Update medicine.yml
---
Resources/Prototypes/Reagents/medicine.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml
index 31d6b66fd00..e87f05b4444 100644
--- a/Resources/Prototypes/Reagents/medicine.yml
+++ b/Resources/Prototypes/Reagents/medicine.yml
@@ -97,8 +97,10 @@
damage:
types:
Poison: -3
+ # ADT-Tweak-Start
- !type:AdjustAllergicStack
amount: -1
+ # ADT-Tweak-End
- type: reagent
id: Ethylredoxrazine