From fcb5d0f1152607454809d8d5672b85627d7eb618 Mon Sep 17 00:00:00 2001
From: A-Mironov <65418470+Alexnov33X@users.noreply.github.com>
Date: Wed, 21 Jan 2026 21:18:48 +0300
Subject: [PATCH 1/7] test
---
.../_Sunrise/Disease/UI/DiseaseInfoBui.cs | 49 ++++++++
.../Disease/UI/DiseaseInfoWindow.xaml | 37 ++++++
.../Disease/UI/DiseaseInfoWindow.xaml.cs | 25 ++++
.../_Sunrise/Disease/DiseaseRoleSystem.cs | 118 +++++++++++++++---
Content.Server/_Sunrise/Disease/SickSystem.cs | 1 +
.../Disease/SmallDiseaseRuleSystem.cs | 102 +++++++++++++++
.../Components/EntityStorageComponent.cs | 3 +-
.../_Sunrise/Disease/CureDiseaseInfection.cs | 11 +-
.../_Sunrise/Disease/DiseaseInfoState.cs | 24 ++++
.../_Sunrise/Disease/DiseaseInfoUiKey.cs | 9 ++
.../_Sunrise/Disease/DiseaseRoleComponent.cs | 2 +-
.../Disease/SmallDiseaseRuleComponent.cs | 13 ++
.../_Sunrise/Catalog/disease_catalog.yml | 21 ++--
.../_Sunrise/GameRules/smalldisease.yml | 5 +
.../_Sunrise/Roles/Antags/disease.yml | 2 +
15 files changed, 385 insertions(+), 37 deletions(-)
create mode 100644 Content.Client/_Sunrise/Disease/UI/DiseaseInfoBui.cs
create mode 100644 Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
create mode 100644 Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml.cs
create mode 100644 Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
create mode 100644 Content.Shared/_Sunrise/Disease/DiseaseInfoState.cs
create mode 100644 Content.Shared/_Sunrise/Disease/DiseaseInfoUiKey.cs
create mode 100644 Content.Shared/_Sunrise/Disease/SmallDiseaseRuleComponent.cs
create mode 100644 Resources/Prototypes/_Sunrise/GameRules/smalldisease.yml
diff --git a/Content.Client/_Sunrise/Disease/UI/DiseaseInfoBui.cs b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoBui.cs
new file mode 100644
index 00000000000..57111da707f
--- /dev/null
+++ b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoBui.cs
@@ -0,0 +1,49 @@
+using Content.Shared._Sunrise.Disease;
+using JetBrains.Annotations;
+using Robust.Client.GameObjects;
+using Robust.Client.UserInterface;
+
+namespace Content.Client._Sunrise.Disease.UI;
+
+[UsedImplicitly]
+public sealed class DiseaseInfoBui : BoundUserInterface
+{
+ [ViewVariables]
+ private DiseaseInfoWindow? _window;
+
+ public DiseaseInfoBui(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+ }
+
+ protected override void Open()
+ {
+ base.Open();
+ _window = new DiseaseInfoWindow();
+ _window.OnClose += Close;
+ _window.OpenCentered();
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (state is not DiseaseInfoState diseaseState)
+ return;
+
+ _window?.UpdateState(
+ diseaseState.BaseInfectChance,
+ diseaseState.CoughSneezeInfectChance,
+ diseaseState.Lethal,
+ diseaseState.Shield,
+ diseaseState.CurrentInfected,
+ diseaseState.TotalInfected
+ );
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (disposing)
+ _window?.Dispose();
+ }
+}
diff --git a/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
new file mode 100644
index 00000000000..d57ad7a6526
--- /dev/null
+++ b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml.cs b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml.cs
new file mode 100644
index 00000000000..21ec833a69e
--- /dev/null
+++ b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml.cs
@@ -0,0 +1,25 @@
+using Content.Client.UserInterface.Controls;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.CustomControls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client._Sunrise.Disease.UI;
+
+[GenerateTypedNameReferences]
+public sealed partial class DiseaseInfoWindow : DefaultWindow
+{
+ public DiseaseInfoWindow()
+ {
+ RobustXamlLoader.Load(this);
+ }
+
+ public void UpdateState(float baseChance, float infectChance, int lethal, int shield, int currentInfected, int totalInfected)
+ {
+ BaseChanceLabel.Text = (baseChance * 100).ToString("F0") + "%";
+ InfectChanceLabel.Text = (infectChance * 100).ToString("F0") + "%";
+ LethalLabel.Text = lethal.ToString();
+ ShieldLabel.Text = shield.ToString();
+ CurrentInfectedLabel.Text = currentInfected.ToString();
+ TotalInfectedLabel.Text = totalInfected.ToString();
+ }
+}
diff --git a/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs b/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs
index d28de0aa333..6f8f7a6b219 100644
--- a/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs
+++ b/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs
@@ -19,6 +19,8 @@
namespace Content.Server._Sunrise.Disease;
+using Robust.Server.GameObjects;
+
public sealed class DiseaseRoleSystem : SharedDiseaseRoleSystem
{
[Dependency] private readonly IRobustRandom _random = default!;
@@ -29,6 +31,7 @@ public sealed class DiseaseRoleSystem : SharedDiseaseRoleSystem
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly UserInterfaceSystem _ui = default!;
private static readonly List _bloodReagents = new()
{
@@ -76,6 +79,7 @@ private void OnInfects(InfectEvent args)
// Play Initial Infected antag audio (only for the disease player)
_audio.PlayGlobal("/Audio/Ambience/Antag/zombie_start.ogg", args.Performer);
+ UpdateUi(args.Performer, component);
}
}
@@ -105,25 +109,21 @@ private void OnShop(EntityUid uid, DiseaseRoleComponent component, DiseaseShopAc
private void OnDiseaseInfo(EntityUid uid, DiseaseRoleComponent component, DiseaseInfoEvent args)
{
- // Create a simple formatted display using StringBuilder for better performance
- var infoText = new StringBuilder();
- infoText.AppendLine(Loc.GetString("disease-info-header"));
- infoText.AppendLine();
-
- // Core Statistics Section
- infoText.AppendLine(Loc.GetString("disease-info-core-statistics") + ":");
- infoText.AppendLine("├─ " + Loc.GetString("disease-info-base-chance") + ": " + (component.BaseInfectChance * 100).ToString("F0") + "%");
- infoText.AppendLine("├─ " + Loc.GetString("disease-info-cough-sneeze-chance") + ": " + (component.CoughSneezeInfectChance * 100).ToString("F0") + "%");
- infoText.AppendLine("├─ " + Loc.GetString("disease-info-lethal") + ": " + component.Lethal);
- infoText.AppendLine("└─ " + Loc.GetString("disease-info-shield") + ": " + component.Shield);
- infoText.AppendLine();
-
- // Infection Statistics Section
- infoText.AppendLine(Loc.GetString("disease-info-infection-statistics") + ":");
- infoText.AppendLine("├─ " + Loc.GetString("disease-info-infected-count") + ": " + component.Infected.Count);
- infoText.Append("└─ " + Loc.GetString("disease-info-total-infected") + ": " + component.SickOfAllTime);
-
- _popup.PopupEntity(infoText.ToString(), uid, uid, PopupType.Large);
+ _ui.TryToggleUi(uid, DiseaseInfoUiKey.Key, uid);
+ UpdateUi(uid, component);
+ }
+
+ private void UpdateUi(EntityUid uid, DiseaseRoleComponent component)
+ {
+ var state = new DiseaseInfoState(
+ component.BaseInfectChance,
+ component.CoughSneezeInfectChance,
+ component.Lethal,
+ component.Shield,
+ component.Infected.Count,
+ component.SickOfAllTime
+ );
+ _ui.SetUiState(uid, DiseaseInfoUiKey.Key, state);
}
@@ -221,10 +221,84 @@ private void OnStorePurchase(ref StoreBuyFinishedEvent args)
_popup.PopupEntity(Loc.GetString("disease-upgrade-max-reached"), storeOwner, PopupType.Medium);
}
break;
+ default:
+ if (!diseaseComp.Symptoms.ContainsKey(args.PurchasedItem.ID))
+ {
+ // Check if it's a symptom listing from the ID
+ var symptom = args.PurchasedItem.ID;
+ int minLevel = 0;
+ int maxLevel = 5;
+
+ // Ideally we should get this from the listing/action metadata,
+ // but since the previous system relied on hardcoded event args from actions,
+ // we might need to assume or look it up.
+ // For now, let's replicate the levels from the removed actions:
+
+ switch (symptom)
+ {
+ case "Cough": minLevel = 2; break;
+ case "Sneeze": minLevel = 3; break;
+ case "Vomit": minLevel = 3; break;
+ case "Narcolepsy": minLevel = 3; break;
+ case "Crying": minLevel = 0; break;
+ case "Muted": minLevel = 4; break;
+ case "Slowness": minLevel = 2; break;
+ case "Bleed": minLevel = 3; break;
+ case "Blindness": minLevel = 4; break;
+ case "Insult": minLevel = 2; break;
+ // Zombie handled separately via special event if needed, or if it's just a symptom?
+ // The original action raised DiseaseZombieEvent for "Zombie".
+ // Wait, "Zombie" was a separate category in catalog?
+ // Checking catalog: "Zombie" listing productAction was ActionDiseaseZombie.
+ // So Zombie is NOT handled here in default block if we want to keep specific behavior.
+ }
+
+ if (symptom == "Zombie")
+ {
+ // Zombie Logic - previously handled by DiseaseZombieEvent which was raised by InstantAction
+ // Now we trigger it directly on purchase
+ // We need to manually construct the event or call the logic.
+ // But wait, the original logic was: Purchase -> Get Item/Action -> Use Action -> Trigger Event.
+ // If we enable "instant buy", we trigger logic now.
+ var zombieArgs = new DiseaseZombieEvent(); // Empty event, needed for handler signature?
+ // Actually the handler uses the event args to get the action to remove it.
+ // We can just extract the logic.
+
+ var infected = diseaseComp.Infected.ToArray();
+ var convertedCount = 0;
+
+ for (int i = 0; i < infected.Length; i++)
+ {
+ var target = infected[i];
+ if (target.IsValid() && !Deleted(target))
+ {
+ RemComp(target);
+ diseaseComp.Infected.Remove(target);
+ EnsureComp(target);
+ EnsureComp(target);
+ convertedCount++;
+ }
+ }
+
+ if (convertedCount > 0)
+ {
+ _popup.PopupEntity(Loc.GetString("disease-zombie-success", ("count", convertedCount)), storeOwner, PopupType.Medium);
+ }
+ }
+ else
+ {
+ // Regular Symptom
+ diseaseComp.Symptoms.Add(symptom, new SymptomData(minLevel, maxLevel));
+ _popup.PopupEntity(Loc.GetString("disease-upgrade-purchased"), storeOwner, PopupType.Medium);
+ }
+ }
+ break;
}
+ UpdateUi(storeOwner, diseaseComp);
+
}
- void AddMoney(EntityUid uid, FixedPoint2 value)
+ private void AddMoney(EntityUid uid, FixedPoint2 value)
{
if (TryComp(uid, out var diseaseComp))
{
@@ -313,6 +387,10 @@ private void OnInfectedDeath(MobStateChangedEvent args)
if (Deleted(args.Target))
return;
+ // Check if the dying entity was actually infected
+ if (!HasComp(args.Target))
+ return;
+
// Reward all other disease antagonists when any infected dies
var diseaseQuery = EntityQueryEnumerator();
while (diseaseQuery.MoveNext(out var diseaseUid, out var diseaseComp))
diff --git a/Content.Server/_Sunrise/Disease/SickSystem.cs b/Content.Server/_Sunrise/Disease/SickSystem.cs
index e9bfce2510e..7810f8bd841 100644
--- a/Content.Server/_Sunrise/Disease/SickSystem.cs
+++ b/Content.Server/_Sunrise/Disease/SickSystem.cs
@@ -153,6 +153,7 @@ public override void Update(float frameTime)
RaiseNetworkEvent(new ClientInfectEvent(GetNetEntity(uid), GetNetEntity(component.owner)));
diseaseComp.SickOfAllTime++;
AddMoney(component.owner, 5);
+ _popupSystem.PopupEntity(Loc.GetString("disease-infect-reward", ("points", 5)), component.owner, component.owner, PopupType.Medium);
component.Inited = true;
}
diff --git a/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs b/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
new file mode 100644
index 00000000000..a218be54e27
--- /dev/null
+++ b/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
@@ -0,0 +1,102 @@
+using Content.Shared._Sunrise.Disease;
+using Content.Server.GameTicking.Rules;
+using Content.Shared.GameTicking.Components;
+using Robust.Server.GameObjects;
+using Robust.Shared.Random;
+using Robust.Shared.Prototypes;
+using Content.Shared.Store;
+using System.Linq;
+using Content.Shared.Humanoid;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Mind;
+using Content.Shared.Mind.Components;
+using Robust.Shared.Map;
+
+namespace Content.Server._Sunrise.Disease;
+
+public sealed class SmallDiseaseRuleSystem : GameRuleSystem
+{
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly EntityLookupSystem _lookup = default!;
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+
+ protected override void Started(EntityUid uid, SmallDiseaseRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
+ {
+ base.Started(uid, component, gameRule, args);
+
+ // 1. Find potential victims (Humanoid, Has Mind, Not Dead, Not Sick)
+ var query = EntityQueryEnumerator();
+ var candidates = new List();
+
+ while (query.MoveNext(out var entity, out _, out _, out _, out var xform))
+ {
+ if (HasComp(entity))
+ continue;
+
+ // Simple check to ensure they are on a station/grid generally
+ if (xform.GridUid == null)
+ continue;
+
+ candidates.Add(entity);
+ }
+
+ if (candidates.Count == 0)
+ return;
+
+ // 2. Spawn the Disease Entity (Dummy)
+ var diseaseUid = Spawn("MobDisease", MapCoordinates.Nullspace);
+ if (!TryComp(diseaseUid, out var diseaseComp))
+ {
+ return;
+ }
+
+ // 3. Configure Symptoms
+ // Always add Cough
+ diseaseComp.Symptoms.TryAdd("Cough", new SymptomData(2, 5)); // Cough is level 2 typically
+
+ var currentPoints = 0;
+ var availableSymptoms = new List();
+
+ // Gather all symptom listings
+ foreach (var listing in _prototypeManager.EnumeratePrototypes())
+ {
+ if (listing.Categories.Contains("DiseaseSymptomsCategory"))
+ {
+ availableSymptoms.Add(listing);
+ }
+ }
+
+ // Randomly add until 50 points
+ // Safety loop
+ for (int i = 0; i < 20 && currentPoints < component.TargetSymptomPoints; i++)
+ {
+ if (availableSymptoms.Count == 0) break;
+
+ var pick = _random.Pick(availableSymptoms);
+ var cost = pick.Cost.GetValueOrDefault("DiseasePoints", 0);
+
+ // Avoid adding same symptom twice
+ if (!diseaseComp.Symptoms.ContainsKey(pick.ID))
+ {
+ // Determine level (rough approximation or default)
+ // We'll use 0-5 as safe defaults or check ID specific logic if strictly needed
+ // Using 1-5 generic
+ diseaseComp.Symptoms.Add(pick.ID, new SymptomData(1, 5));
+ currentPoints += cost.Int();
+ }
+ }
+
+ // 4. Infect Targets
+ _random.Shuffle(candidates);
+ var targetCount = Math.Min(component.TargetInfectedCount, candidates.Count);
+
+ for (int i = 0; i < targetCount; i++)
+ {
+ var victim = candidates[i];
+ var sick = EnsureComp(victim);
+ sick.owner = diseaseUid;
+ diseaseComp.Infected.Add(victim);
+ }
+ }
+}
diff --git a/Content.Shared/Storage/Components/EntityStorageComponent.cs b/Content.Shared/Storage/Components/EntityStorageComponent.cs
index ecfcccc45b7..107555514d1 100644
--- a/Content.Shared/Storage/Components/EntityStorageComponent.cs
+++ b/Content.Shared/Storage/Components/EntityStorageComponent.cs
@@ -42,7 +42,8 @@ public sealed partial class EntityStorageComponent : Component, IGasMixtureHolde
public int MasksToRemove = (int)(
CollisionGroup.MidImpassable |
CollisionGroup.HighImpassable |
- CollisionGroup.LowImpassable);
+ CollisionGroup.LowImpassable |
+ CollisionGroup.BulletImpassable);
///
/// Collision masks that were removed from ANY layer when the storage was opened;
diff --git a/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs b/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs
index 43d8d33c854..3695406e5f5 100644
--- a/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs
+++ b/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs
@@ -11,11 +11,14 @@ public sealed partial class CureDiseaseInfectionEntityEffectSystem : EntityEffec
protected override void Effect(Entity entity, ref EntityEffectEvent args)
{
- if (_entityManager.TryGetComponent(entity.Owner, out var disease))
+ if (_entityManager.TryGetComponent(entity.Owner, out var sick))
{
- var comp = _entityManager.EnsureComponent(entity.Owner);
- comp.Immune = args.Effect.Innoculate;
- comp.Delay = TimeSpan.FromMinutes(2) + TimeSpan.FromSeconds(disease.Shield * 30);
+ if (_entityManager.TryGetComponent(sick.owner, out var disease))
+ {
+ var comp = _entityManager.EnsureComponent(entity.Owner);
+ comp.Immune = args.Effect.Innoculate;
+ comp.Delay = TimeSpan.FromMinutes(2) + TimeSpan.FromSeconds(disease.Shield * 30);
+ }
}
}
}
diff --git a/Content.Shared/_Sunrise/Disease/DiseaseInfoState.cs b/Content.Shared/_Sunrise/Disease/DiseaseInfoState.cs
new file mode 100644
index 00000000000..5f052648060
--- /dev/null
+++ b/Content.Shared/_Sunrise/Disease/DiseaseInfoState.cs
@@ -0,0 +1,24 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._Sunrise.Disease;
+
+[Serializable, NetSerializable]
+public sealed class DiseaseInfoState : BoundUserInterfaceState
+{
+ public float BaseInfectChance;
+ public float CoughSneezeInfectChance;
+ public int Lethal;
+ public int Shield;
+ public int CurrentInfected;
+ public int TotalInfected;
+
+ public DiseaseInfoState(float baseInfectChance, float coughSneezeInfectChance, int lethal, int shield, int currentInfected, int totalInfected)
+ {
+ BaseInfectChance = baseInfectChance;
+ CoughSneezeInfectChance = coughSneezeInfectChance;
+ Lethal = lethal;
+ Shield = shield;
+ CurrentInfected = currentInfected;
+ TotalInfected = totalInfected;
+ }
+}
diff --git a/Content.Shared/_Sunrise/Disease/DiseaseInfoUiKey.cs b/Content.Shared/_Sunrise/Disease/DiseaseInfoUiKey.cs
new file mode 100644
index 00000000000..ee161ef9429
--- /dev/null
+++ b/Content.Shared/_Sunrise/Disease/DiseaseInfoUiKey.cs
@@ -0,0 +1,9 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared._Sunrise.Disease;
+
+[Serializable, NetSerializable]
+public enum DiseaseInfoUiKey
+{
+ Key
+}
diff --git a/Content.Shared/_Sunrise/Disease/DiseaseRoleComponent.cs b/Content.Shared/_Sunrise/Disease/DiseaseRoleComponent.cs
index 414552cf9f5..c15a170efc8 100644
--- a/Content.Shared/_Sunrise/Disease/DiseaseRoleComponent.cs
+++ b/Content.Shared/_Sunrise/Disease/DiseaseRoleComponent.cs
@@ -38,7 +38,7 @@ public sealed partial class DiseaseRoleComponent : Component
[DataField] public int SickOfAllTime = 0;
[DataField("newBloodReagent")]
- public List NewBloodReagent = new() { "ZombieBlood" };
+ public List NewBloodReagent = new();
}
[Serializable, NetSerializable]
diff --git a/Content.Shared/_Sunrise/Disease/SmallDiseaseRuleComponent.cs b/Content.Shared/_Sunrise/Disease/SmallDiseaseRuleComponent.cs
new file mode 100644
index 00000000000..d9af32c2b96
--- /dev/null
+++ b/Content.Shared/_Sunrise/Disease/SmallDiseaseRuleComponent.cs
@@ -0,0 +1,13 @@
+using Content.Shared.GameTicking.Components;
+
+namespace Content.Shared._Sunrise.Disease;
+
+[RegisterComponent]
+public sealed partial class SmallDiseaseRuleComponent : Component
+{
+ [DataField]
+ public int TargetInfectedCount = 3;
+
+ [DataField]
+ public int TargetSymptomPoints = 50;
+}
diff --git a/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml b/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
index 8d58cabd4cc..7b8a2c35da6 100644
--- a/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
+++ b/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
@@ -4,7 +4,6 @@
description: listing-disease-cough-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: cough }
raiseProductEventOnUser: true
- productAction: ActionDiseaseCough
cost:
DiseasePoints: 0
categories:
@@ -18,7 +17,7 @@
name: listing-disease-sneeze-name
description: listing-disease-sneeze-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: sneeze }
- productAction: ActionDiseaseSneeze
+ raiseProductEventOnUser: true
cost:
DiseasePoints: 20
categories:
@@ -32,7 +31,7 @@
name: listing-disease-vomit-name
description: listing-disease-vomit-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: vomit }
- productAction: ActionDiseaseVomit
+ raiseProductEventOnUser: true
cost:
DiseasePoints: 25
categories:
@@ -46,7 +45,7 @@
name: listing-disease-narcolepsy-name
description: listing-disease-narcolepsy-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: narcolepsy }
- productAction: ActionDiseaseNarcolepsy
+ raiseProductEventOnUser: true
cost:
DiseasePoints: 20
categories:
@@ -60,7 +59,7 @@
name: listing-disease-cry-name
description: listing-disease-cry-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: sob }
- productAction: ActionDiseaseCrying
+ raiseProductEventOnUser: true
cost:
DiseasePoints: 10
categories:
@@ -74,7 +73,7 @@
name: listing-disease-muted-name
description: listing-disease-muted-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: muted }
- productAction: ActionDiseaseMuted
+ raiseProductEventOnUser: true
cost:
DiseasePoints: 25
categories:
@@ -88,7 +87,7 @@
name: listing-disease-slowness-name
description: listing-disease-slowness-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: slowness }
- productAction: ActionDiseaseSlowness
+ raiseProductEventOnUser: true
cost:
DiseasePoints: 15
categories:
@@ -102,7 +101,7 @@
name: listing-disease-bleed-name
description: listing-disease-bleed-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: bleed }
- productAction: ActionDiseaseBleed
+ raiseProductEventOnUser: true
cost:
DiseasePoints: 30
categories:
@@ -116,7 +115,7 @@
name: listing-disease-blindness-name
description: listing-disease-blindness-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: blindness }
- productAction: ActionDiseaseBlindness
+ raiseProductEventOnUser: true
cost:
DiseasePoints: 40
categories:
@@ -130,7 +129,7 @@
name: listing-disease-insult-name
description: listing-disease-insult-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: lethal }
- productAction: ActionDiseaseInsult
+ raiseProductEventOnUser: true
cost:
DiseasePoints: 20
categories:
@@ -144,7 +143,7 @@
name: listing-disease-zombie-name
description: listing-disease-zombie-description
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: lethal }
- productAction: ActionDiseaseZombie
+ raiseProductEventOnUser: true
cost:
DiseasePoints: 200
categories:
diff --git a/Resources/Prototypes/_Sunrise/GameRules/smalldisease.yml b/Resources/Prototypes/_Sunrise/GameRules/smalldisease.yml
new file mode 100644
index 00000000000..2da18d2a88a
--- /dev/null
+++ b/Resources/Prototypes/_Sunrise/GameRules/smalldisease.yml
@@ -0,0 +1,5 @@
+- type: entity
+ id: SmallDisease
+ parent: BaseGameRule
+ components:
+ - type: SmallDiseaseRule
diff --git a/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml b/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml
index 90029d47c48..b469c1d09ac 100644
--- a/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml
+++ b/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml
@@ -60,6 +60,8 @@
interfaces:
enum.StoreUiKey.Key:
type: StoreBoundUserInterface
+ enum.DiseaseInfoUiKey.Key:
+ type: DiseaseInfoBui
- type: Visibility
layer: 2 #ghost vis layer
- type: Store
From fb12cc300d9e9533dc368e4fc519e9f795a3e2f4 Mon Sep 17 00:00:00 2001
From: A-Mironov <65418470+Alexnov33X@users.noreply.github.com>
Date: Thu, 22 Jan 2026 02:10:19 +0300
Subject: [PATCH 2/7] looks good
---
.../_Sunrise/Disease/DiseaseRoleSystem.cs | 9 +-
Content.Server/_Sunrise/Disease/SickSystem.cs | 16 +-
.../Disease/SmallDiseaseRuleSystem.cs | 8 +-
.../_strings/_sunrise/disease/disease.ftl | 6 +-
.../_strings/_sunrise/disease/disease.ftl | 6 +-
.../_Sunrise/Catalog/disease_catalog.yml | 8 +-
.../_Sunrise/Roles/Antags/disease.yml | 2 +-
.../Guidebook/_Sunrise/Antagonist/Disease.xml | 208 +++++++++++-------
8 files changed, 159 insertions(+), 104 deletions(-)
diff --git a/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs b/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs
index 6f8f7a6b219..13f0e0746fc 100644
--- a/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs
+++ b/Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs
@@ -69,13 +69,8 @@ private void OnInfects(InfectEvent args)
{
if (TryComp(args.Performer, out var component))
{
- if (TryRemoveMoney(args.Performer, component.InfectCost))
- OnInfect(args, 1.0f);
- else
- {
- _popup.PopupEntity(Loc.GetString("disease-not-enough-evolution-points"), args.Performer, PopupType.Medium);
- return;
- }
+ OnInfect(args, 1.0f);
+ _popup.PopupEntity(Loc.GetString("disease-infect-success"), args.Performer, PopupType.Medium);
// Play Initial Infected antag audio (only for the disease player)
_audio.PlayGlobal("/Audio/Ambience/Antag/zombie_start.ogg", args.Performer);
diff --git a/Content.Server/_Sunrise/Disease/SickSystem.cs b/Content.Server/_Sunrise/Disease/SickSystem.cs
index 7810f8bd841..39466de9eea 100644
--- a/Content.Server/_Sunrise/Disease/SickSystem.cs
+++ b/Content.Server/_Sunrise/Disease/SickSystem.cs
@@ -34,6 +34,8 @@
using Content.Shared.Store.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Chemistry.Components;
+using Content.Shared.Inventory;
+using Content.Shared.Zombies;
namespace Content.Server._Sunrise.Disease;
public sealed class SickSystem : SharedSickSystem
{
@@ -223,7 +225,7 @@ private void UpdateInfection(EntityUid uid, SickComponent component, EntityUid d
if (!HasComp(uid))
{
var c = AddComp(uid);
- EntityManager.EntitySysManager.GetEntitySystem().SetNarcolepsy(uid, new Vector2(10, 30), new Vector2(300, 600), c);
+ EntityManager.EntitySysManager.GetEntitySystem().SetNarcolepsy(uid, new Vector2(60, 80), new Vector2(8, 12), c);
}
break;
case "Muted":
@@ -273,7 +275,11 @@ private void OnEmote(EntityUid uid, SickComponent component, ref EmoteEvent args
{
if (HasComp(entity) && !HasComp(entity) && !HasComp(entity))
{
- OnInfected(entity, component.owner, disease.CoughSneezeInfectChance);
+ var ev = new ZombificationResistanceQueryEvent(SlotFlags.HEAD | SlotFlags.MASK | SlotFlags.OUTERCLOTHING);
+ RaiseLocalEvent(entity, ev);
+
+ if (_robustRandom.Prob(ev.TotalCoefficient))
+ OnInfected(entity, component.owner, disease.CoughSneezeInfectChance);
}
}
}
@@ -291,7 +297,11 @@ private void OnEmote(EntityUid uid, SickComponent component, ref EmoteEvent args
{
if (HasComp(entity) && !HasComp(entity) && !HasComp(entity))
{
- OnInfected(entity, component.owner, disease.CoughSneezeInfectChance);
+ var ev = new ZombificationResistanceQueryEvent(SlotFlags.HEAD | SlotFlags.MASK | SlotFlags.OUTERCLOTHING);
+ RaiseLocalEvent(entity, ev);
+
+ if (_robustRandom.Prob(ev.TotalCoefficient))
+ OnInfected(entity, component.owner, disease.CoughSneezeInfectChance);
}
}
}
diff --git a/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs b/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
index a218be54e27..e2bd6b8436d 100644
--- a/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
+++ b/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
@@ -47,9 +47,8 @@ protected override void Started(EntityUid uid, SmallDiseaseRuleComponent compone
// 2. Spawn the Disease Entity (Dummy)
var diseaseUid = Spawn("MobDisease", MapCoordinates.Nullspace);
if (!TryComp(diseaseUid, out var diseaseComp))
- {
return;
- }
+
// 3. Configure Symptoms
// Always add Cough
@@ -74,7 +73,10 @@ protected override void Started(EntityUid uid, SmallDiseaseRuleComponent compone
if (availableSymptoms.Count == 0) break;
var pick = _random.Pick(availableSymptoms);
- var cost = pick.Cost.GetValueOrDefault("DiseasePoints", 0);
+ var cost = pick.Cost.GetValueOrDefault("DiseasePoints", 100);
+
+ if (cost > component.TargetSymptomPoints)
+ continue;
// Avoid adding same symptom twice
if (!diseaseComp.Symptoms.ContainsKey(pick.ID))
diff --git a/Resources/Locale/en-US/_strings/_sunrise/disease/disease.ftl b/Resources/Locale/en-US/_strings/_sunrise/disease/disease.ftl
index 89078f907c7..9be33ef20c7 100644
--- a/Resources/Locale/en-US/_strings/_sunrise/disease/disease.ftl
+++ b/Resources/Locale/en-US/_strings/_sunrise/disease/disease.ftl
@@ -36,4 +36,8 @@ disease-infect-charge-max-reached = Already at maximum charges ({ $maxCharges })
disease-death-reward = Received { $points } Disease Points from another disease's death!
disease-upgrade-purchased = Upgrade purchased successfully!
-disease-upgrade-max-reached = Maximum upgrade level reached!
\ No newline at end of file
+disease-upgrade-max-reached = Maximum upgrade level reached!
+
+disease-infect-success = Successful infection!
+
+disease-info-window-title = Disease Statistics
diff --git a/Resources/Locale/ru-RU/_strings/_sunrise/disease/disease.ftl b/Resources/Locale/ru-RU/_strings/_sunrise/disease/disease.ftl
index 5469a6b695f..93fa934cce3 100644
--- a/Resources/Locale/ru-RU/_strings/_sunrise/disease/disease.ftl
+++ b/Resources/Locale/ru-RU/_strings/_sunrise/disease/disease.ftl
@@ -36,4 +36,8 @@ disease-infect-charge-max-reached = Уже максимальное количе
disease-death-reward = Получено { $points } очков болезни от смерти другого больного!
disease-upgrade-purchased = Улучшение успешно куплено!
-disease-upgrade-max-reached = Достигнут максимальный уровень улучшения!
\ No newline at end of file
+disease-upgrade-max-reached = Достигнут максимальный уровень улучшения!
+
+disease-infect-success = Успешное заражение!
+
+disease-info-window-title = Статистика Болезни
diff --git a/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml b/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
index 7b8a2c35da6..d74c3702732 100644
--- a/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
+++ b/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
@@ -175,7 +175,7 @@
- DiseaseEvolutionCategory
conditions:
- !type:ListingLimitedStockCondition
- stock: 1
+ stock: 5
- type: listing
id: InfectChance
@@ -189,7 +189,7 @@
- DiseaseEvolutionCategory
conditions:
- !type:ListingLimitedStockCondition
- stock: 1
+ stock: 3
- type: listing
id: Shield
@@ -203,7 +203,7 @@
- DiseaseEvolutionCategory
conditions:
- !type:ListingLimitedStockCondition
- stock: 1
+ stock: 6
- type: listing
id: Lethal
@@ -217,5 +217,5 @@
- DiseaseEvolutionCategory
conditions:
- !type:ListingLimitedStockCondition
- stock: 1
+ stock: 5
#Sunrise-End
diff --git a/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml b/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml
index b469c1d09ac..802785d2528 100644
--- a/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml
+++ b/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml
@@ -10,7 +10,7 @@
context: "ghost"
- type: MovementSpeedModifier
baseWalkSpeed: 6
- baseSprintSpeed: 6
+ baseSprintSpeed: 20
- type: Sprite
noRot: true
drawdepth: Ghosts
diff --git a/Resources/ServerInfo/Guidebook/_Sunrise/Antagonist/Disease.xml b/Resources/ServerInfo/Guidebook/_Sunrise/Antagonist/Disease.xml
index 973718657b7..6305b58216b 100644
--- a/Resources/ServerInfo/Guidebook/_Sunrise/Antagonist/Disease.xml
+++ b/Resources/ServerInfo/Guidebook/_Sunrise/Antagonist/Disease.xml
@@ -1,87 +1,127 @@
- # Разумная болезнь
-
-
- [color=#999999][italic]"Ты просто простудился... или они уже думают за тебя?"[/italic][/color]
-
-
-
-
-
- Разумная болезнь — это антагонист, способный заражать всех существ, а так-же передаваться, она имеет свойство "мутировать", у нее появляються разные симптомы, увеличиваеться летальность, заразность и устойчивость к лекартсвам. она легко распростроняеться если не принять необходимые меры.
-
- ## Основные симптомы
-
- Первыми отличимыми симптомами которые есть у каждой болезни, это головная боль и головокружение, так-же основной переносный симптом - кашель и чих.
-
- ## Передача
-
- Передача вируса происходит при кашле и чихе, если рядом с вами находиться зараженный и имеет упомянутые симптомы, вы имеете вероятность заразиться. Эта вероятность уменьшаеться при ношении защитных костюмов, масок и прочих средств.
-
- Однако, у болезни есть возможность заразить вас игнорируя упомянутые средства защиты, при этом она использует очки эволюции, что означает что вы в любом случае можете заболеть.
-
- ## Возможные симптомы
-
- Так-же кроме основных симптомов, у болезни есть перечень других, таких как:
-
- - Непроизвольные слёзы(10 ОЭ)
-
- [color=#999999][italic]У заражённых активно слезяться глаза, из-за чего кажется, что они плачут.[/italic][/color]
-
-
- - Изнеможение(15 ОЭ)
-
- [color=#999999][italic]Вирус вызывает разрушение мышечных волокон, приводящее к атрофие и сопровождающееся слабостью. Снижает общую мобильность[/italic][/color]
-
-
- - Сонливость(20 ОЭ)
-
- [color=#999999][italic]У заражённых появляется постоянное желание спать, с которым они иногда не могут справиться.[/italic][/color]
-
-
- - Судороги(20 ОЭ)
-
- [color=#999999][italic]Длительная болезнь вызывает гиперстимуляцию двигательных нейронов, в результате чего больные могут испытывать перенапряжение мышц, приводящие к судорогам.[/italic][/color]
-
-
- - Немота(25 ОЭ)
-
- [color=#999999][italic]Мутация вызывает повреждение подъязычного нерва, приводя к параличу мышц языка, из-за чего больные теряют возможность нормально говорить.[/italic][/color]
-
-
- - Тошнота(25 ОЭ)
-
- [color=#999999][italic]Заражённых начинает тоншить, вызывая рвоту.[/italic][/color]
-
-
- - Кровопотеря(30 ОЭ)
-
- [color=#999999][italic]Вирус вызывает денатурацию гемоглобина крови, из-за чего у всех носителей появляется тяжелая степень анемии.[/italic][/color]
-
-
- - Слепота(40 ОЭ)
-
- [color=#999999][italic]Длительная болезнь приводит к отмиранию зрительного нерва, что приводит к практически полной слепоте больного.[/italic][/color]
-
-
- ## Базовые противодействия
-
- Если вы заметили что у вас появились какие-то из симптом, не стоит сразу бежать в вирусологию, но стоит принять минимальные меры, носить маску, избегать контакта с персоналом.
-
- Если же у вас обноружилось 2 и более семптома, например кашель, головная боль и изнеможение, немедленно отправьтесь в медецинский отдел а так-же оповестите врача о том что вы вероятно заражены.
-
- ## Вакцина
-
- Для создания вакцины необходима пробирка, шприц, здоровый человек, заражённый человек и вакцинатор, делаем всё строго в последовательности:
-
- - Набираем кровь заражённого человека в пробирку
-
- - Вставляем пробирку в вакцинатор
-
- - Получаем бумажку, с указаниями для создания вакцины.
-
- - Делаем всё как указано на листочке.
-
- Готово, вы получили вакцину.
+
+[head=1][color=#ff9966]Разумная болезнь[/color][/head]
+
+
+[color=#999999][italic]"Ты просто простудился... или они уже думают за тебя?"[/italic][/color]
+
+
+
+
+
+[color=#cccccc]Разумная болезнь — это антагонист, способный заражать всех существ и передаваться между ними. Болезнь обладает свойством «мутировать»: у неё появляются разные симптомы, а её летальность, заразность и устойчивость к лекарствам увеличиваются. Она легко распространяется, если не принять необходимые меры.[/color]
+
+[head=2][color=#ffaa66]Основные симптомы[/color][/head]
+
+[color=#dddddd]Первыми отличимыми симптомами, которые есть у каждой болезни, являются головная боль и головокружение. Также основными переносными симптомами являются кашель и чихание.[/color]
+
+[head=2][color=#ffaa66]Передача[/color][/head]
+
+[color=#dddddd]Передача вируса происходит при кашле и чихании. Если рядом с вами находится заражённый, у которого есть упомянутые симптомы, вы с определённой вероятностью можете заразиться. Эта вероятность уменьшается при ношении защитных костюмов, масок и прочих средств.[/color]
+
+[color=#dddddd]Однако у болезни есть возможность заразить вас, игнорируя упомянутые средства защиты, расходуя на это очки эволюции. Это означает, что вы в любом случае можете заболеть.[/color]
+
+[head=2][color=#ffaa66]Возможные симптомы[/color][/head]
+
+[color=#dddddd]Кроме основных симптомов, у болезни есть перечень других, таких как:[/color]
+
+[head=3][color=#ffbb77]Непроизвольные слёзы (10 ОЭ)[/color][/head]
+
+[color=#999999][italic]У заражённых активно слезятся глаза, из-за чего кажется, что они плачут.[/italic][/color]
+
+
+[head=3][color=#ffbb77]Изнеможение (15 ОЭ)[/color][/head]
+
+[color=#999999][italic]Вирус вызывает разрушение мышечных волокон, приводящее к атрофии и сопровождающееся слабостью. Снижает общую мобильность.[/italic][/color]
+
+
+[head=3][color=#ffbb77]Сонливость (20 ОЭ)[/color][/head]
+
+[color=#999999][italic]У заражённых появляется постоянное желание спать, с которым они иногда не могут справиться.[/italic][/color]
+
+
+[head=3][color=#ffbb77]Судороги (20 ОЭ)[/color][/head]
+
+[color=#999999][italic]Длительная болезнь вызывает гиперстимуляцию двигательных нейронов, в результате чего больные могут испытывать перенапряжение мышц, приводящее к судорогам.[/italic][/color]
+
+
+[head=3][color=#ffbb77]Немота (25 ОЭ)[/color][/head]
+
+[color=#999999][italic]Мутация вызывает повреждение подъязычного нерва, приводя к параличу мышц языка, из-за чего больные теряют возможность нормально говорить.[/italic][/color]
+
+
+[head=3][color=#ffbb77]Тошнота (25 ОЭ)[/color][/head]
+
+[color=#999999][italic]Заражённых начинает тошнить, вызывая рвоту.[/italic][/color]
+
+
+[head=3][color=#ffbb77]Кровопотеря (30 ОЭ)[/color][/head]
+
+[color=#999999][italic]Вирус вызывает денатурацию гемоглобина крови, из-за чего у всех носителей появляется тяжёлая степень анемии.[/italic][/color]
+
+
+[head=3][color=#ffbb77]Слепота (40 ОЭ)[/color][/head]
+
+[color=#999999][italic]Длительная болезнь приводит к отмиранию зрительного нерва, что приводит к практически полной слепоте больного.[/italic][/color]
+
+
+[head=2][color=#ffaa66]Базовые противодействия[/color][/head]
+
+[color=#dddddd]Если вы заметили, что у вас появились какие-то из симптомов, не стоит сразу бежать в вирусологию, но стоит принять минимальные меры: носить маску, избегать контакта с персоналом.[/color]
+
+[color=#dddddd]Если же у вас обнаружилось 2 и более симптома, например, кашель, головная боль и изнеможение, немедленно отправьтесь в медицинский отдел, а также оповестите врача о том, что вы, вероятно, заражены.[/color]
+
+[head=2][color=#ffaa66]Вакцина[/color][/head]
+
+[head=3][color=#ffbb77]Получение рецепта[/color][/head]
+
+[color=#dddddd]Используйте бумагу на Вакцинаторе. Вы получите рецепт, который зависит от случайной «Группы крови» болезни в этом раунде.[/color]
+
+
+
+
+
+
+
+
+[head=3][color=#ffbb77]Создание заготовки[/color][/head]
+
+[color=#dddddd]Смешайте указанные в рецепте химикаты с [bold]Кровью заражённого[/bold] (получите у пациента), чтобы создать [color=#83a7b1]Незавершённую вакцину[/color].[/color]
+
+
+
+
+
+
+[head=3][color=#ffbb77]Синтез вакцины (Антивирусина)[/color][/head]
+
+[color=#dddddd]Поместите полученную [color=#83a7b1]Вирусин[/color] и единицу [bold]Чистой крови[/bold] (здорового человека) в Вакцинатор. Он переработает их в [color=#86caf7]Антивирусин[/color] (20ед).[/color]
+
+
+
+[head=3][color=#ffbb77]Антивирусин+[/color][/head]
+
+[color=#dddddd]Можно создать улучшенную версию, смешав обычную [color=#86caf7]Антивирусин[/color] с Криптобиолином, Сигинатом и Кровью заражённого. Она даёт [color=#8192ea]постоянный иммунитет[/color].[/color]
+
+
+
+
+
+
+
+[head=2][color=#ffaa66]Действие вакцины[/color][/head]
+
+[color=#dddddd]При инъекции пациенту (10ед обычной или 7ед Антивирусина+):[/color]
+
+[head=3][color=#ffbb77]Таймер лечения[/color][/head]
+[color=#dddddd]Запускается процесс исцеления. Время зависит от уровня защиты («Щита») болезни: [color=#ff9999]2 минуты + 30 сек за каждый уровень щита[/color].[/color]
+
+[head=3][color=#ffbb77]Побочный эффект[/color][/head]
+[color=#dddddd]Пока вакцина действует, [bold]скорость бега пациента снижается в 2 раза[/bold]. Это делает его уязвимым![/color]
+
+[head=3][color=#ffbb77]Результат[/color][/head]
+[color=#dddddd]По истечении таймера болезнь полностью исчезает. Если использовалась Вакцина+, пациент получает [color=#99ff99]иммунитет[/color].[/color]
+
+[color=#dddddd][bold]Будьте осторожны[/bold]: болезнь может попытаться убить ослабленного пациента, пока вакцина действует![/color]
From 70ff8f4f0a2ac8febeb7a12a9263a64b333fc108 Mon Sep 17 00:00:00 2001
From: A-Mironov <65418470+Alexnov33X@users.noreply.github.com>
Date: Thu, 22 Jan 2026 02:16:59 +0300
Subject: [PATCH 3/7] Fixes
---
.../Disease/UI/DiseaseInfoWindow.xaml | 18 ++++++-------
.../Guidebook/_Sunrise/Antagonist/Disease.xml | 26 +++++++------------
2 files changed, 19 insertions(+), 25 deletions(-)
diff --git a/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
index d57ad7a6526..ee70a34ebbf 100644
--- a/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
+++ b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
@@ -1,36 +1,36 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/Resources/ServerInfo/Guidebook/_Sunrise/Antagonist/Disease.xml b/Resources/ServerInfo/Guidebook/_Sunrise/Antagonist/Disease.xml
index 6305b58216b..ea8a506fdb7 100644
--- a/Resources/ServerInfo/Guidebook/_Sunrise/Antagonist/Disease.xml
+++ b/Resources/ServerInfo/Guidebook/_Sunrise/Antagonist/Disease.xml
@@ -26,44 +26,38 @@
[color=#dddddd]Кроме основных симптомов, у болезни есть перечень других, таких как:[/color]
[head=3][color=#ffbb77]Непроизвольные слёзы (10 ОЭ)[/color][/head]
-
+
[color=#999999][italic]У заражённых активно слезятся глаза, из-за чего кажется, что они плачут.[/italic][/color]
-
+
[head=3][color=#ffbb77]Изнеможение (15 ОЭ)[/color][/head]
-
+
[color=#999999][italic]Вирус вызывает разрушение мышечных волокон, приводящее к атрофии и сопровождающееся слабостью. Снижает общую мобильность.[/italic][/color]
-
+
[head=3][color=#ffbb77]Сонливость (20 ОЭ)[/color][/head]
-
+
[color=#999999][italic]У заражённых появляется постоянное желание спать, с которым они иногда не могут справиться.[/italic][/color]
-
+
[head=3][color=#ffbb77]Судороги (20 ОЭ)[/color][/head]
-
[color=#999999][italic]Длительная болезнь вызывает гиперстимуляцию двигательных нейронов, в результате чего больные могут испытывать перенапряжение мышц, приводящее к судорогам.[/italic][/color]
-
+
[head=3][color=#ffbb77]Немота (25 ОЭ)[/color][/head]
-
[color=#999999][italic]Мутация вызывает повреждение подъязычного нерва, приводя к параличу мышц языка, из-за чего больные теряют возможность нормально говорить.[/italic][/color]
-
[head=3][color=#ffbb77]Тошнота (25 ОЭ)[/color][/head]
-
[color=#999999][italic]Заражённых начинает тошнить, вызывая рвоту.[/italic][/color]
-
+
[head=3][color=#ffbb77]Кровопотеря (30 ОЭ)[/color][/head]
-
[color=#999999][italic]Вирус вызывает денатурацию гемоглобина крови, из-за чего у всех носителей появляется тяжёлая степень анемии.[/italic][/color]
-
+
[head=3][color=#ffbb77]Слепота (40 ОЭ)[/color][/head]
-
[color=#999999][italic]Длительная болезнь приводит к отмиранию зрительного нерва, что приводит к практически полной слепоте больного.[/italic][/color]
-
+
[head=2][color=#ffaa66]Базовые противодействия[/color][/head]
From 02f965570076769fcbd831889908a553513ab71b Mon Sep 17 00:00:00 2001
From: A-Mironov <65418470+Alexnov33X@users.noreply.github.com>
Date: Sat, 24 Jan 2026 23:36:43 +0300
Subject: [PATCH 4/7] =?UTF-8?q?=D0=90=20=D0=B2=D0=B0=D0=BA=D1=86=D0=B8?=
=?UTF-8?q?=D0=BD=D0=B0=20=D0=BD=D0=B5=20=D0=BB=D0=B5=D1=87=D0=B8=D0=BB?=
=?UTF-8?q?=D0=B0=20=D0=B4=D0=BE=20=D0=BA=D0=BE=D0=BD=D1=86=D0=B0=20=D0=BB?=
=?UTF-8?q?=D0=BE=D0=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Content.Server/_Sunrise/Disease/SickSystem.cs | 3 +++
Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs | 3 +++
Content.Shared/_Sunrise/Disease/DiseaseVaccineTimerSystem.cs | 2 +-
Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml | 2 +-
4 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/Content.Server/_Sunrise/Disease/SickSystem.cs b/Content.Server/_Sunrise/Disease/SickSystem.cs
index 39466de9eea..5f90a91c14b 100644
--- a/Content.Server/_Sunrise/Disease/SickSystem.cs
+++ b/Content.Server/_Sunrise/Disease/SickSystem.cs
@@ -117,6 +117,9 @@ public void OnShut(EntityUid uid, SickComponent component, ComponentShutdown arg
solution.AddReagent(reagentId, FixedPoint2.New((int)stream.BloodReferenceSolution.MaxVolume));
}
+ if (solution.Volume == 0)
+ return;
+
_bloodstream.ChangeBloodReagents(uid, solution);
}
}
diff --git a/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs b/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs
index 3695406e5f5..02a6a21630f 100644
--- a/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs
+++ b/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs
@@ -2,12 +2,14 @@
using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
namespace Content.Shared._Sunrise.Disease;
public sealed partial class CureDiseaseInfectionEntityEffectSystem : EntityEffectSystem
{
[Dependency] private readonly EntityManager _entityManager = default!;
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
protected override void Effect(Entity entity, ref EntityEffectEvent args)
{
@@ -18,6 +20,7 @@ protected override void Effect(Entity entity, ref EntityEffectEve
var comp = _entityManager.EnsureComponent(entity.Owner);
comp.Immune = args.Effect.Innoculate;
comp.Delay = TimeSpan.FromMinutes(2) + TimeSpan.FromSeconds(disease.Shield * 30);
+ comp.ReadyAt = _gameTiming.CurTime + comp.Delay;
}
}
}
diff --git a/Content.Shared/_Sunrise/Disease/DiseaseVaccineTimerSystem.cs b/Content.Shared/_Sunrise/Disease/DiseaseVaccineTimerSystem.cs
index 0eceeb571d8..07f5721b5a7 100644
--- a/Content.Shared/_Sunrise/Disease/DiseaseVaccineTimerSystem.cs
+++ b/Content.Shared/_Sunrise/Disease/DiseaseVaccineTimerSystem.cs
@@ -45,7 +45,7 @@ public override void Update(float frameTime)
if (!HasComp(uid))
{
RemComp(uid);
- return;
+ continue;
}
RemComp(uid);
diff --git a/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml b/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml
index 802785d2528..b4e199192c1 100644
--- a/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml
+++ b/Resources/Prototypes/_Sunrise/Roles/Antags/disease.yml
@@ -10,7 +10,7 @@
context: "ghost"
- type: MovementSpeedModifier
baseWalkSpeed: 6
- baseSprintSpeed: 20
+ baseSprintSpeed: 10
- type: Sprite
noRot: true
drawdepth: Ghosts
From 74733fbc418f95a89469b34aa31fdda9f61f58ed Mon Sep 17 00:00:00 2001
From: A-Mironov <65418470+Alexnov33X@users.noreply.github.com>
Date: Wed, 21 Jan 2026 21:18:48 +0300
Subject: [PATCH 5/7] test
---
.../_Sunrise/Disease/UI/DiseaseInfoWindow.xaml | 18 +++++++++---------
.../_Sunrise/Disease/SmallDiseaseRuleSystem.cs | 8 +++-----
.../_Sunrise/Disease/CureDiseaseInfection.cs | 3 ---
3 files changed, 12 insertions(+), 17 deletions(-)
diff --git a/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
index ee70a34ebbf..d57ad7a6526 100644
--- a/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
+++ b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
@@ -1,36 +1,36 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs b/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
index e2bd6b8436d..a218be54e27 100644
--- a/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
+++ b/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
@@ -47,8 +47,9 @@ protected override void Started(EntityUid uid, SmallDiseaseRuleComponent compone
// 2. Spawn the Disease Entity (Dummy)
var diseaseUid = Spawn("MobDisease", MapCoordinates.Nullspace);
if (!TryComp(diseaseUid, out var diseaseComp))
+ {
return;
-
+ }
// 3. Configure Symptoms
// Always add Cough
@@ -73,10 +74,7 @@ protected override void Started(EntityUid uid, SmallDiseaseRuleComponent compone
if (availableSymptoms.Count == 0) break;
var pick = _random.Pick(availableSymptoms);
- var cost = pick.Cost.GetValueOrDefault("DiseasePoints", 100);
-
- if (cost > component.TargetSymptomPoints)
- continue;
+ var cost = pick.Cost.GetValueOrDefault("DiseasePoints", 0);
// Avoid adding same symptom twice
if (!diseaseComp.Symptoms.ContainsKey(pick.ID))
diff --git a/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs b/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs
index 02a6a21630f..3695406e5f5 100644
--- a/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs
+++ b/Content.Shared/_Sunrise/Disease/CureDiseaseInfection.cs
@@ -2,14 +2,12 @@
using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;
-using Robust.Shared.Timing;
namespace Content.Shared._Sunrise.Disease;
public sealed partial class CureDiseaseInfectionEntityEffectSystem : EntityEffectSystem
{
[Dependency] private readonly EntityManager _entityManager = default!;
- [Dependency] private readonly IGameTiming _gameTiming = default!;
protected override void Effect(Entity entity, ref EntityEffectEvent args)
{
@@ -20,7 +18,6 @@ protected override void Effect(Entity entity, ref EntityEffectEve
var comp = _entityManager.EnsureComponent(entity.Owner);
comp.Immune = args.Effect.Innoculate;
comp.Delay = TimeSpan.FromMinutes(2) + TimeSpan.FromSeconds(disease.Shield * 30);
- comp.ReadyAt = _gameTiming.CurTime + comp.Delay;
}
}
}
From c807ee33e0098c9fe4118e59bd9b3acd830fda3a Mon Sep 17 00:00:00 2001
From: A-Mironov <65418470+Alexnov33X@users.noreply.github.com>
Date: Sat, 14 Mar 2026 11:15:49 +0300
Subject: [PATCH 6/7] =?UTF-8?q?=D0=90=D0=BD=D0=BE=D0=BD=D1=81=20=D0=B4?=
=?UTF-8?q?=D0=BB=D1=8F=20=D1=82=D1=83=D0=BF=D0=BE=D0=B9=20=D0=B1=D0=BE?=
=?UTF-8?q?=D0=BB=D0=B5=D0=B7=D0=BD=D0=B8=20+=20=D0=B7=D0=BE=D0=BC=D0=B1?=
=?UTF-8?q?=D0=B8=20=D0=BE=D1=87=D0=BA=D0=B8=20=D0=BF=D0=BE=D0=B2=D1=8B?=
=?UTF-8?q?=D1=88=D0=B5=D0=BD=D1=8B=20=D0=B4=D0=BE=20500?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../_Sunrise/Disease/SmallDiseaseRuleSystem.cs | 14 +++++++++++---
.../_Sunrise/Catalog/disease_catalog.yml | 2 +-
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs b/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
index a218be54e27..31df2924b62 100644
--- a/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
+++ b/Content.Server/_Sunrise/Disease/SmallDiseaseRuleSystem.cs
@@ -1,16 +1,15 @@
using Content.Shared._Sunrise.Disease;
using Content.Server.GameTicking.Rules;
using Content.Shared.GameTicking.Components;
-using Robust.Server.GameObjects;
using Robust.Shared.Random;
using Robust.Shared.Prototypes;
using Content.Shared.Store;
-using System.Linq;
using Content.Shared.Humanoid;
using Content.Shared.Mobs.Components;
-using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Robust.Shared.Map;
+using Content.Server.Chat.Systems;
+using Robust.Shared.Timing;
namespace Content.Server._Sunrise.Disease;
@@ -20,11 +19,20 @@ public sealed class SmallDiseaseRuleSystem : GameRuleSystem
+ {
+ var message = Loc.GetString("disease-biohazard-announcement");
+ var sender = Loc.GetString("disease-biohazard-announcement-sender");
+
+ _chatSystem.DispatchGlobalAnnouncement(message, sender, playDefault: true, colorOverride: Color.Red);
+ });
+
// 1. Find potential victims (Humanoid, Has Mind, Not Dead, Not Sick)
var query = EntityQueryEnumerator();
var candidates = new List();
diff --git a/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml b/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
index d74c3702732..932785b775d 100644
--- a/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
+++ b/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
@@ -145,7 +145,7 @@
icon: { sprite: /Textures/_Sunrise/Actions/disease.rsi, state: lethal }
raiseProductEventOnUser: true
cost:
- DiseasePoints: 200
+ DiseasePoints: 500
categories:
- DiseaseSymptomsCategory
conditions:
From 45614a4263e494c0099a80c74a7ef8046eb330b5 Mon Sep 17 00:00:00 2001
From: A-Mironov <65418470+Alexnov33X@users.noreply.github.com>
Date: Sat, 14 Mar 2026 12:19:16 +0300
Subject: [PATCH 7/7] =?UTF-8?q?=D0=9F=D0=BE=20=D0=BF=D1=80=D0=BE=D1=81?=
=?UTF-8?q?=D1=8C=D0=B1=D0=B5=20=D0=BA=D1=80=D0=BE=D0=BB=D1=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../_Sunrise/Disease/UI/DiseaseInfoWindow.xaml | 18 +++++++++---------
Content.Server/_Sunrise/Disease/SickSystem.cs | 17 +++++++++++------
.../_Sunrise/Catalog/disease_catalog.yml | 2 +-
3 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
index d57ad7a6526..ee70a34ebbf 100644
--- a/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
+++ b/Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
@@ -1,36 +1,36 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/Content.Server/_Sunrise/Disease/SickSystem.cs b/Content.Server/_Sunrise/Disease/SickSystem.cs
index 5f90a91c14b..385c6b65483 100644
--- a/Content.Server/_Sunrise/Disease/SickSystem.cs
+++ b/Content.Server/_Sunrise/Disease/SickSystem.cs
@@ -3,26 +3,20 @@
using Robust.Shared.Timing;
using Content.Shared._Sunrise.Disease;
using System.Numerics;
-using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chat.Systems;
-using Content.Shared.Interaction.Events;
using Robust.Server.GameObjects;
-using Robust.Shared.Map;
using Robust.Shared.Random;
using Content.Shared.Humanoid;
-using Content.Server.Store.Components;
using Content.Server.Store.Systems;
using Content.Server.Popups;
using Content.Shared.Popups;
using Content.Server.Chat;
using Content.Shared.Stunnable;
using Content.Shared.Damage.Prototypes;
-using Content.Shared.Damage;
using Content.Server.Emoting.Systems;
using Content.Server.Speech.EntitySystems;
using Content.Shared.FixedPoint;
-using Content.Server.Medical;
using Content.Server.Traits.Assorted;
using Content.Shared.Body.Components;
using Content.Shared.Chat;
@@ -50,6 +44,7 @@ public sealed class SickSystem : SharedSickSystem
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedStunSystem _stun = default!;
+ [Dependency] private readonly UserInterfaceSystem _ui = default!;
private EntityLookupSystem Lookup => _entityManager.System();
public override void Initialize()
{
@@ -160,6 +155,16 @@ public override void Update(float frameTime)
AddMoney(component.owner, 5);
_popupSystem.PopupEntity(Loc.GetString("disease-infect-reward", ("points", 5)), component.owner, component.owner, PopupType.Medium);
+ var state = new DiseaseInfoState(
+ diseaseComp.BaseInfectChance,
+ diseaseComp.CoughSneezeInfectChance,
+ diseaseComp.Lethal,
+ diseaseComp.Shield,
+ diseaseComp.Infected.Count,
+ diseaseComp.SickOfAllTime
+ );
+ _ui.SetUiState(component.owner, DiseaseInfoUiKey.Key, state);
+
component.Inited = true;
}
else
diff --git a/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml b/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
index 932785b775d..cb28b2e06ae 100644
--- a/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
+++ b/Resources/Prototypes/_Sunrise/Catalog/disease_catalog.yml
@@ -7,7 +7,7 @@
cost:
DiseasePoints: 0
categories:
- - DiseaseInfectCategory
+ - DiseaseSymptomsCategory
conditions:
- !type:ListingLimitedStockCondition
stock: 1