+++ /dev/null
-using Content.Shared.Atmos.Miasma;
-using Robust.Shared.GameStates;
-
-namespace Content.Client.Atmos.Miasma;
-
-[NetworkedComponent, RegisterComponent]
-public sealed class FliesComponent : SharedFliesComponent
-{ }
+++ /dev/null
-using Robust.Client.GameObjects;
-using Robust.Shared.Utility;
-
-namespace Content.Client.Atmos.Miasma;
-
-public sealed class FliesSystem : EntitySystem
-{
- public override void Initialize()
- {
- SubscribeLocalEvent<FliesComponent, ComponentStartup>(FliesAdded);
- SubscribeLocalEvent<FliesComponent, ComponentShutdown>(FliesRemoved);
- }
-
- private void FliesRemoved(EntityUid uid, FliesComponent component, ComponentShutdown args)
- {
- if (!TryComp<SpriteComponent>(uid, out var sprite))
- return;
-
- if (!sprite.LayerMapTryGet(FliesKey.Key, out var layer))
- return;
-
- sprite.RemoveLayer(layer);
- }
-
- private void FliesAdded(EntityUid uid, FliesComponent component, ComponentStartup args)
- {
- if (!TryComp<SpriteComponent>(uid, out var sprite))
- return;
-
- if (sprite.LayerMapTryGet(FliesKey.Key, out var _))
- return;
-
- var layer = sprite.AddLayer(new SpriteSpecifier.Rsi(new ("Objects/Misc/flies.rsi"), "flies"));
- sprite.LayerMapSet(FliesKey.Key, layer);
- }
-
- private enum FliesKey
- {
- Key,
- }
-}
+++ /dev/null
-using Robust.Client.GameObjects;
-using Content.Shared.Disease;
-
-namespace Content.Client.Disease
-{
- /// <summary>
- /// Controls client-side visuals for the
- /// disease machines.
- /// </summary>
- public sealed class DiseaseMachineSystem : VisualizerSystem<DiseaseMachineVisualsComponent>
- {
- protected override void OnAppearanceChange(EntityUid uid, DiseaseMachineVisualsComponent component, ref AppearanceChangeEvent args)
- {
- if (args.Sprite == null)
- return;
-
- if (AppearanceSystem.TryGetData<bool>(uid, DiseaseMachineVisuals.IsOn, out var isOn, args.Component)
- && AppearanceSystem.TryGetData<bool>(uid, DiseaseMachineVisuals.IsRunning, out var isRunning, args.Component))
- {
- var state = isRunning ? component.RunningState : component.IdleState;
- args.Sprite.LayerSetVisible(DiseaseMachineVisualLayers.IsOn, isOn);
- args.Sprite.LayerSetState(DiseaseMachineVisualLayers.IsRunning, state);
- }
- }
- }
-}
-public enum DiseaseMachineVisualLayers : byte
-{
- IsOn,
- IsRunning
-}
+++ /dev/null
-namespace Content.Client.Disease;
-
-/// <summary>
-/// Holds the idle and running state for machines to control
-/// playing animtions on the client.
-/// </summary>
-[RegisterComponent]
-public sealed class DiseaseMachineVisualsComponent : Component
-{
- [DataField("idleState", required: true)]
- public string IdleState = default!;
-
- [DataField("runningState", required: true)]
- public string RunningState = default!;
-}
using System.Text;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
-using Content.Shared.Disease.Components;
using Content.Shared.FixedPoint;
using Content.Shared.IdentityManagement;
using Content.Shared.MedicalScanner;
entities.TryGetComponent<MetaDataComponent>(msg.TargetEntity.Value, out var metaData))
entityName = Identity.Name(msg.TargetEntity.Value, entities);
- IReadOnlyDictionary<string, FixedPoint2> DamagePerGroup = damageable.DamagePerGroup;
- IReadOnlyDictionary<string, FixedPoint2> DamagePerType = damageable.Damage.DamageDict;
+ IReadOnlyDictionary<string, FixedPoint2> damagePerGroup = damageable.DamagePerGroup;
+ IReadOnlyDictionary<string, FixedPoint2> damagePerType = damageable.Damage.DamageDict;
text.Append($"{Loc.GetString("health-analyzer-window-entity-health-text", ("entityName", entityName))}\n");
- // Status Effects / Components
- if (entities.HasComponent<DiseasedComponent>(msg.TargetEntity))
- {
- text.Append($"{Loc.GetString("disease-scanner-diseased")}\n");
- }else
- {
- text.Append($"{Loc.GetString("disease-scanner-not-diseased")}\n");
- }
-
// Damage
text.Append($"\n{Loc.GetString("health-analyzer-window-entity-damage-total-text", ("amount", damageable.TotalDamage))}\n");
var protos = IoCManager.Resolve<IPrototypeManager>();
// Show the total damage and type breakdown for each damage group.
- foreach (var (damageGroupId, damageAmount) in DamagePerGroup)
+ foreach (var (damageGroupId, damageAmount) in damagePerGroup)
{
text.Append($"\n{Loc.GetString("health-analyzer-window-damage-group-text", ("damageGroup", Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId)), ("amount", damageAmount))}");
// Show the damage for each type in that group.
var group = protos.Index<DamageGroupPrototype>(damageGroupId);
foreach (var type in group.DamageTypes)
{
- if (DamagePerType.TryGetValue(type, out var typeAmount))
+ if (damagePerType.TryGetValue(type, out var typeAmount))
{
// If damage types are allowed to belong to more than one damage group, they may appear twice here. Mark them as duplicate.
if (!shownTypes.Contains(type))
--- /dev/null
+namespace Content.Client.Medical;
+
+/* Here be dragons */
+
+public enum DiseaseMachineVisualLayers : byte
+{
+ IsOn,
+ IsRunning
+}
+++ /dev/null
-using System.Threading.Tasks;
-using Content.Server.Disease;
-using Content.Shared.Disease;
-using NUnit.Framework;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Prototypes;
-
-namespace Content.IntegrationTests.Tests.Disease;
-
-[TestFixture]
-[TestOf(typeof(DiseaseSystem))]
-public sealed class DeviceNetworkTest
-{
- [Test]
- public async Task AddAllDiseases()
- {
- await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
- var server = pairTracker.Pair.Server;
- var testMap = await PoolManager.CreateTestMap(pairTracker);
- await server.WaitPost(() =>
- {
- var protoManager = IoCManager.Resolve<IPrototypeManager>();
- var entManager = IoCManager.Resolve<IEntityManager>();
- var entSysManager = IoCManager.Resolve<IEntitySystemManager>();
- var diseaseSystem = entSysManager.GetEntitySystem<DiseaseSystem>();
-
- var sickEntity = entManager.SpawnEntity("MobHuman", testMap.GridCoords);
- foreach (var diseaseProto in protoManager.EnumeratePrototypes<DiseasePrototype>())
- {
- diseaseSystem.TryAddDisease(sickEntity, diseaseProto);
- }
- });
- }
-}
+++ /dev/null
-using System.Threading.Tasks;
-using Content.Shared.Disease;
-using NUnit.Framework;
-using Robust.Shared.Prototypes;
-
-namespace Content.IntegrationTests.Tests;
-
-[TestFixture]
-public sealed class DiseaseTest
-{
- /// <summary>
- /// Asserts that a disease prototype has valid stages for its effects and cures.
- /// </summary>
- [Test]
- public async Task Stages()
- {
- await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true});
- var server = pairTracker.Pair.Server;
-
- var protoManager = server.ResolveDependency<IPrototypeManager>();
-
- await server.WaitAssertion(() =>
- {
- foreach (var proto in protoManager.EnumeratePrototypes<DiseasePrototype>())
- {
- var stagesLength = proto.Stages.Count;
-
- foreach (var effect in proto.Effects)
- {
- for (var i = 0; i < effect.Stages.Length; i++)
- {
- Assert.That(i >= 0 && i < stagesLength, $"Disease {proto.ID} has an effect with an incorrect stage, {i}!");
- }
- }
-
- foreach (var cure in proto.Cures)
- {
- for (var i = 0; i < cure.Stages.Length; i++)
- {
- Assert.That(i >= 0 && i < stagesLength, $"Disease {proto.ID} has a cure with an incorrect stage, {i}!");
- }
- }
- }
- });
-
- await pairTracker.CleanReturnAsync();
- }
-}
-using Content.Server.Atmos.Miasma;
-using Content.Server.Body.Components;
-using Content.Server.Body.Systems;
-using Content.Server.Disease.Components;
-using Content.Server.Disease;
-using Content.Server.Nutrition.Components;
-using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Administration;
-using Content.Shared.Damage;
-using Content.Shared.Jittering;
-using Content.Shared.Nutrition.Components;
using Content.Shared.Rejuvenate;
-using Content.Shared.StatusEffect;
using Robust.Server.Player;
using Robust.Shared.Console;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Damage.Systems;
-using Content.Server.Disease;
-using Content.Server.Disease.Components;
using Content.Server.Electrocution;
using Content.Server.Explosion.EntitySystems;
using Content.Server.GhostKick;
-using Content.Server.Interaction.Components;
using Content.Server.Medical;
using Content.Server.Nutrition.EntitySystems;
using Content.Server.Pointing.Components;
using Content.Shared.Clothing.Components;
using Content.Shared.Damage;
using Content.Shared.Database;
-using Content.Shared.Disease;
using Content.Shared.Electrocution;
using Content.Shared.Interaction.Components;
using Content.Shared.Inventory;
using Content.Shared.Tabletop.Components;
using Content.Shared.Verbs;
using Robust.Server.GameObjects;
-using Robust.Server.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Utility;
-using Robust.Shared.Audio;
using Timer = Robust.Shared.Timing.Timer;
using Content.Shared.Cluwne;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly CreamPieSystem _creamPieSystem = default!;
- [Dependency] private readonly DiseaseSystem _diseaseSystem = default!;
[Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!;
[Dependency] private readonly EntityStorageSystem _entityStorageSystem = default!;
[Dependency] private readonly ExplosionSystem _explosionSystem = default!;
};
args.Verbs.Add(disposalBin);
- if (TryComp<DiseaseCarrierComponent>(args.Target, out var carrier))
- {
- Verb lungCancer = new()
- {
- Text = "Lung Cancer",
- Category = VerbCategory.Smite,
- Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Species/Human/organs.rsi"), "lung-l"),
- Act = () =>
- {
- _diseaseSystem.TryInfect(carrier, _prototypeManager.Index<DiseasePrototype>("StageIIIALungCancer"),
- 1.0f, true);
- },
- Impact = LogImpact.Extreme,
- Message = Loc.GetString("admin-smite-lung-cancer-description")
- };
- args.Verbs.Add(lungCancer);
- }
-
if (TryComp<DamageableComponent>(args.Target, out var damageable) &&
HasComp<MobStateComponent>(args.Target))
{
+++ /dev/null
-using Content.Shared.Atmos.Miasma;
-using Robust.Shared.GameStates;
-
-namespace Content.Server.Atmos.Miasma;
-
-[NetworkedComponent, RegisterComponent]
-public sealed class FliesComponent : SharedFliesComponent
-{
- /// Need something to hold the ambient sound, at least until that system becomes more robust
- public EntityUid VirtFlies;
-}
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly TransformSystem _transform = default!;
- /// Miasma Disease Pool
- /// Miasma outbreaks are not per-entity,
- /// so this ensures that each entity in the same incident
- /// receives the same disease.
-
- public readonly IReadOnlyList<string> MiasmaDiseasePool = new[]
- {
- "VentCough",
- "AMIV",
- "SpaceCold",
- "SpaceFlu",
- "BirdFlew",
- "VanAusdallsRobovirus",
- "BleedersBite",
- "Plague",
- "TongueTwister",
- "MemeticAmirmir"
- };
-
- /// <summary>
- /// The current pool disease.
- /// </summary>
- private string _poolDisease = "";
-
- /// <summary>
- /// The target time it waits until..
- /// After that, it resets current time + _poolRepickTime.
- /// Any infection will also reset it to current time + _poolRepickTime.
- /// </summary>
- private TimeSpan _diseaseTime = TimeSpan.FromMinutes(5);
-
- /// <summary>
- /// How long without an infection before we pick a new disease.
- /// </summary>
- private readonly TimeSpan _poolRepickTime = TimeSpan.FromMinutes(5);
-
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RottingComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<RottingComponent, RejuvenateEvent>(OnRejuvenate);
- SubscribeLocalEvent<FliesComponent, ComponentInit>(OnFliesInit);
- SubscribeLocalEvent<FliesComponent, ComponentShutdown>(OnFliesShutdown);
-
SubscribeLocalEvent<TemperatureComponent, IsRottingEvent>(OnTempIsRotting);
-
- // Init disease pool
- _poolDisease = _random.Pick(MiasmaDiseasePool);
}
private void OnPerishableUnpaused(EntityUid uid, PerishableComponent component, ref EntityUnpausedEvent args)
private void OnShutdown(EntityUid uid, RottingComponent component, ComponentShutdown args)
{
- RemComp<FliesComponent>(uid);
if (TryComp<PerishableComponent>(uid, out var perishable))
{
perishable.NextPerishUpdate = TimeSpan.Zero;
RemCompDeferred<RottingComponent>(uid);
}
- /// Containers
-
-
- #region Fly stuff
- private void OnFliesInit(EntityUid uid, FliesComponent component, ComponentInit args)
- {
- component.VirtFlies = Spawn("AmbientSoundSourceFlies", Transform(uid).Coordinates);
- }
-
- private void OnFliesShutdown(EntityUid uid, FliesComponent component, ComponentShutdown args)
- {
- if (!Terminating(uid) && !Deleted(uid))
- Del(component.VirtFlies);
- }
- #endregion
-
private void OnTempIsRotting(EntityUid uid, TemperatureComponent component, ref IsRottingEvent args)
{
if (args.Handled)
args.Handled = component.CurrentTemperature > Atmospherics.T0C + 0.85f;
}
- public string RequestPoolDisease()
- {
- // We reset the current time on this outbreak so people don't get unlucky at the transition time
- _diseaseTime = _timing.CurTime + _poolRepickTime;
- return _poolDisease;
- }
-
public override void Update(float frameTime)
{
base.Update(frameTime);
- if (_timing.CurTime >= _diseaseTime)
- {
- _diseaseTime = _timing.CurTime + _poolRepickTime;
- _poolDisease = _random.Pick(MiasmaDiseasePool);
- }
-
var perishQuery = EntityQueryEnumerator<PerishableComponent>();
while (perishQuery.MoveNext(out var uid, out var perishable))
{
{
var rot = AddComp<RottingComponent>(uid);
rot.NextRotUpdate = _timing.CurTime + rot.RotUpdateRate;
- EnsureComp<FliesComponent>(uid);
}
-
}
var rotQuery = EntityQueryEnumerator<RottingComponent, PerishableComponent, TransformComponent>();
+++ /dev/null
-using Content.Shared.Chemistry.Reagent;
-using Content.Server.Disease;
-using Content.Shared.Disease;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using JetBrains.Annotations;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- /// <summary>
- /// Default metabolism for medicine reagents.
- /// </summary>
- [UsedImplicitly]
- public sealed class ChemCauseDisease : ReagentEffect
- {
- /// <summary>
- /// Chance it has each tick to cause disease, between 0 and 1
- /// </summary>
- [DataField("causeChance")]
- public float CauseChance = 0.15f;
-
- /// <summary>
- /// The disease to add.
- /// </summary>
- [DataField("disease", customTypeSerializer: typeof(PrototypeIdSerializer<DiseasePrototype>), required: true)]
- [ViewVariables(VVAccess.ReadWrite)]
- public string Disease = default!;
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (args.Scale != 1f)
- return;
-
- EntitySystem.Get<DiseaseSystem>().TryAddDisease(args.SolutionEntity, Disease);
- }
- }
-}
+++ /dev/null
-using Content.Shared.Chemistry.Reagent;
-using Content.Server.Disease;
-using Content.Shared.Disease.Components;
-using Robust.Shared.Random;
-using JetBrains.Annotations;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- /// <summary>
- /// Causes a random disease from a list, if the user is not already diseased.
- /// </summary>
- [UsedImplicitly]
- public sealed class ChemCauseRandomDisease : ReagentEffect
- {
- /// <summary>
- /// A disease to choose from.
- /// </summary>
- [DataField("diseases", required: true)]
- [ViewVariables(VVAccess.ReadWrite)]
- public List<string> Diseases = default!;
-
- public override void Effect(ReagentEffectArgs args)
- {
- if (args.EntityManager.TryGetComponent<DiseasedComponent>(args.SolutionEntity, out var diseased))
- return;
-
- if (args.Scale != 1f)
- return;
-
- var random = IoCManager.Resolve<IRobustRandom>();
-
- EntitySystem.Get<DiseaseSystem>().TryAddDisease(args.SolutionEntity, random.Pick(Diseases));
- }
- }
-}
+++ /dev/null
-using Content.Shared.Chemistry.Reagent;
-using Content.Server.Disease;
-using JetBrains.Annotations;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- /// <summary>
- /// Default metabolism for medicine reagents.
- /// </summary>
- [UsedImplicitly]
- public sealed class ChemCureDisease : ReagentEffect
- {
- /// <summary>
- /// Chance it has each tick to cure a disease, between 0 and 1
- /// </summary>
- [DataField("cureChance")]
- public float CureChance = 0.15f;
-
- public override void Effect(ReagentEffectArgs args)
- {
- var cureChance = CureChance;
-
- cureChance *= args.Scale;
-
- var ev = new CureDiseaseAttemptEvent(cureChance);
- args.EntityManager.EventBus.RaiseLocalEvent(args.SolutionEntity, ev, false);
- }
- }
-}
+++ /dev/null
-using Content.Shared.Chemistry.Reagent;
-using JetBrains.Annotations;
-using Content.Server.Atmos.Miasma;
-using Content.Server.Disease;
-
-namespace Content.Server.Chemistry.ReagentEffects
-{
- /// <summary>
- /// The miasma system rotates between 1 disease at a time.
- /// This gives all entities the disease the miasme system is currently on.
- /// For things ingested by one person, you probably want ChemCauseRandomDisease instead.
- /// </summary>
- [UsedImplicitly]
- public sealed class ChemMiasmaPoolSource : ReagentEffect
- {
- public override void Effect(ReagentEffectArgs args)
- {
- if (args.Scale != 1f)
- return;
-
- string disease = EntitySystem.Get<RottingSystem>().RequestPoolDisease();
-
- EntitySystem.Get<DiseaseSystem>().TryAddDisease(args.SolutionEntity, disease);
- }
- }
-}
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Clothing.Components;
-using Content.Server.Disease.Components;
using Content.Server.IdentityManagement;
using Content.Server.Nutrition.EntitySystems;
using Content.Server.Popups;
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
-using Robust.Shared.Player;
namespace Content.Server.Clothing
{
private void ToggleMaskComponents(EntityUid uid, MaskComponent mask, EntityUid wearer, bool isEquip = false)
{
- //toggle visuals
- if (TryComp<ClothingComponent>(mask.Owner, out var clothing))
+ // toggle visuals
+ if (TryComp<ClothingComponent>(uid, out var clothing))
{
//TODO: sprites for 'pulled down' state. defaults to invisible due to no sprite with this prefix
_clothing.SetEquippedPrefix(uid, mask.IsToggled ? "toggled" : null, clothing);
if (TryComp<IngestionBlockerComponent>(uid, out var blocker))
blocker.Enabled = !mask.IsToggled;
- // toggle disease protection
- if (TryComp<DiseaseProtectionComponent>(uid, out var diseaseProtection))
- diseaseProtection.IsActive = !mask.IsToggled;
-
// toggle identity
if (TryComp<IdentityBlockerComponent>(uid, out var identity))
identity.Enabled = !mask.IsToggled;
+++ /dev/null
-using System.Linq;
-using Content.Shared.Disease;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
-
-namespace Content.Server.Disease.Components
-{
- /// <summary>
- /// Allows the entity to be infected with diseases.
- /// Please use only on mobs.
- /// </summary>
- [RegisterComponent]
- public sealed class DiseaseCarrierComponent : Component
- {
- /// <summary>
- /// Shows the CURRENT diseases on the carrier
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- public List<DiseasePrototype> Diseases = new();
-
- /// <summary>
- /// The carrier's resistance to disease
- /// </summary>
- [DataField("diseaseResist")]
- [ViewVariables(VVAccess.ReadWrite)]
- public float DiseaseResist = 0f;
-
- /// <summary>
- /// Diseases the carrier has had, used for immunity.
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- public List<DiseasePrototype> PastDiseases = new();
-
- /// <summary>
- /// All the diseases the carrier has or has had.
- /// Checked against when trying to add a disease
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- public List<DiseasePrototype> AllDiseases => PastDiseases.Concat(Diseases).ToList();
-
- /// <summary>
- /// A list of diseases which the entity does not
- /// exhibit direct symptoms from. They still transmit
- /// these diseases, just without symptoms.
- /// </summary>
- [DataField("carrierDiseases", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<DiseasePrototype>))]
- public HashSet<string>? CarrierDiseases;
-
- /// <summary>
- /// When this component is initialized,
- /// these diseases will be added to past diseases,
- /// rendering them immune.
- /// </summary>
- [DataField("naturalImmunities")]
- public List<string>? NaturalImmunities;
- }
-}
+++ /dev/null
-namespace Content.Server.Disease.Components
-{
- /// <summary>
- /// To give the disease diagnosing machine specific behavior
- /// </summary>
- [RegisterComponent]
- public sealed class DiseaseDiagnoserComponent : Component
- {}
-}
+++ /dev/null
-using Content.Shared.Disease;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Disease.Components
-{
- /// <summary>
- /// For shared behavior between both disease machines
- /// </summary>
- [RegisterComponent]
- public sealed class DiseaseMachineComponent : Component
- {
- [DataField("delay")]
- public float Delay = 5f;
- /// <summary>
- /// How much time we've accumulated processing
- /// </summary>
- [DataField("accumulator")]
- public float Accumulator = 0f;
- /// <summary>
- /// The disease prototype currently being diagnosed
- /// </summary>
- [ViewVariables]
- public DiseasePrototype? Disease;
- /// <summary>
- /// What the machine will spawn
- /// </summary>
- [DataField("machineOutput", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>), required: true)]
- public string MachineOutput = string.Empty;
- }
-}
+++ /dev/null
-namespace Content.Server.Disease.Components
-{
- /// <summary>
- /// For EntityQuery to keep track of which machines are running
- /// </summary>
- [RegisterComponent]
- public sealed class DiseaseMachineRunningComponent : Component
- {}
-}
+++ /dev/null
-namespace Content.Server.Disease.Components
-{
- /// <summary>
- /// Value added to clothing to give its wearer
- /// protection against infection from diseases
- /// </summary>
- [RegisterComponent]
- public sealed class DiseaseProtectionComponent : Component
- {
- /// <summary>
- /// Float value between 0 and 1, will be subtracted
- /// from the infection chance (which is base 0.7)
- /// Reference guide is a full biosuit w/gloves & mask
- /// should add up to exactly 0.7
- /// </summary>
- [DataField("protection")]
- public float Protection = 0.1f;
- /// <summary>
- /// Is the component currently being worn and affecting someone's disease
- /// resistance? Making the unequip check not totally CBT
- /// </summary>
- public bool IsActive = false;
- }
-}
+++ /dev/null
-using Content.Shared.Disease;
-
-namespace Content.Server.Disease.Components
-{
- /// <summary>
- /// For mouth swabs used to collect and process
- /// disease samples.
- /// </summary>
- [RegisterComponent]
- public sealed class DiseaseSwabComponent : Component
- {
- /// <summary>
- /// How long it takes to swab someone.
- /// </summary>
- [DataField("swabDelay")]
- public float SwabDelay = 2f;
- /// <summary>
- /// If this swab has been used
- /// </summary>
- public bool Used = false;
- /// <summary>
- /// The disease prototype currently on the swab
- /// </summary>
- [ViewVariables]
- public DiseasePrototype? Disease;
- }
-}
+++ /dev/null
-using System.Threading;
-using Content.Shared.Disease;
-
-namespace Content.Server.Disease.Components
-{
- /// <summary>
- /// For disease vaccines
- /// </summary>
- [RegisterComponent]
- public sealed class DiseaseVaccineComponent : Component
- {
- /// <summary>
- /// How long it takes to inject someone
- /// </summary>
- [DataField("injectDelay")]
- public float InjectDelay = 2f;
- /// <summary>
- /// If this vaccine has been used
- /// </summary>
- public bool Used = false;
-
- /// <summary>
- /// The disease prototype currently on the vaccine
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- public DiseasePrototype? Disease;
- }
-}
+++ /dev/null
-namespace Content.Server.Disease.Components
-{
- /// <summary>
- /// Controls disease machine behavior specific to the
- /// vaccine creating machine
- /// </summary>
- [RegisterComponent]
- public sealed class DiseaseVaccineCreatorComponent : Component
- {}
-}
+++ /dev/null
-using Content.Server.Bed.Components;
-using Content.Shared.Bed.Sleep;
-using Content.Shared.Buckle.Components;
-using Content.Shared.Disease;
-
-namespace Content.Server.Disease.Cures
-{
- /// <summary>
- /// Cures the disease after a certain amount of time
- /// strapped.
- /// </summary>
- public sealed class DiseaseBedrestCure : DiseaseCure
- {
- [ViewVariables(VVAccess.ReadWrite)]
- public int Ticker = 0;
-
- /// How many extra ticks you get for sleeping.
- [DataField("sleepMultiplier")]
- public int SleepMultiplier = 3;
-
- [DataField("maxLength", required: true)]
- [ViewVariables(VVAccess.ReadWrite)]
- public int MaxLength = 60;
-
- public override bool Cure(DiseaseEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent<BuckleComponent>(args.DiseasedEntity, out var buckle) ||
- !args.EntityManager.HasComponent<HealOnBuckleComponent>(buckle.BuckledTo))
- return false;
-
- var ticks = 1;
- if (args.EntityManager.HasComponent<SleepingComponent>(args.DiseasedEntity))
- ticks *= SleepMultiplier;
-
- if (buckle.Buckled)
- Ticker += ticks;
- return Ticker >= MaxLength;
- }
-
- public override string CureText()
- {
- return (Loc.GetString("diagnoser-cure-bedrest", ("time", MaxLength), ("sleep", (MaxLength / SleepMultiplier))));
- }
- }
-}
+++ /dev/null
-using Content.Server.Temperature.Components;
-using Content.Shared.Disease;
-
-namespace Content.Server.Disease.Cures
-{
- /// <summary>
- /// Cures the disease if temperature is within certain bounds.
- /// </summary>
- public sealed class DiseaseBodyTemperatureCure : DiseaseCure
- {
- [DataField("min")]
- public float Min = 0;
-
- [DataField("max")]
- public float Max = float.MaxValue;
- public override bool Cure(DiseaseEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent(args.DiseasedEntity, out TemperatureComponent? temp))
- return false;
-
- return temp.CurrentTemperature > Min && temp.CurrentTemperature < float.MaxValue;
- }
-
- public override string CureText()
- {
- if (Min == 0)
- return Loc.GetString("diagnoser-cure-temp-max", ("max", Math.Round(Max)));
- if (Max == float.MaxValue)
- return Loc.GetString("diagnoser-cure-temp-min", ("min", Math.Round(Min)));
-
- return Loc.GetString("diagnoser-cure-temp-both", ("max", Math.Round(Max)), ("min", Math.Round(Min)));
- }
- }
-}
+++ /dev/null
-using Content.Shared.Disease;
-
-namespace Content.Server.Disease.Cures
-{
- /// <summary>
- /// Automatically removes the disease after a
- /// certain amount of time.
- /// </summary>
- public sealed class DiseaseJustWaitCure : DiseaseCure
- {
- /// <summary>
- /// All of these are in seconds
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- public int Ticker = 0;
- [DataField("maxLength", required: true)]
- [ViewVariables(VVAccess.ReadWrite)]
- public int MaxLength = 150;
-
- public override bool Cure(DiseaseEffectArgs args)
- {
- Ticker++;
- return Ticker >= MaxLength;
- }
-
- public override string CureText()
- {
- return Loc.GetString("diagnoser-cure-wait", ("time", MaxLength));
- }
- }
-}
+++ /dev/null
-using Content.Shared.Disease;
-using Content.Shared.FixedPoint;
-using Content.Shared.Chemistry.Reagent;
-using Content.Server.Body.Components;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Disease.Cures
-{
- /// <summary>
- /// Cures the disease if a certain amount of reagent
- /// is in the host's chemstream.
- /// </summary>
- public sealed class DiseaseReagentCure : DiseaseCure
- {
- [DataField("min")]
- public FixedPoint2 Min = 5;
- [DataField("reagent")]
- public string? Reagent;
-
- public override bool Cure(DiseaseEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent<BloodstreamComponent>(args.DiseasedEntity, out var bloodstream))
- return false;
-
- var quant = FixedPoint2.Zero;
- if (Reagent != null && bloodstream.ChemicalSolution.ContainsReagent(Reagent))
- {
- quant = bloodstream.ChemicalSolution.GetReagentQuantity(Reagent);
- }
- return quant >= Min;
- }
-
- public override string CureText()
- {
- var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
- if (Reagent == null || !prototypeMan.TryIndex<ReagentPrototype>(Reagent, out var reagentProt))
- return string.Empty;
- return (Loc.GetString("diagnoser-cure-reagent", ("units", Min), ("reagent", reagentProt.LocalizedName)));
- }
- }
-}
+++ /dev/null
-using Content.Server.Disease.Components;
-using Content.Server.Nutrition.EntitySystems;
-using Content.Server.Paper;
-using Content.Server.Popups;
-using Content.Server.Power.Components;
-using Content.Server.Power.EntitySystems;
-using Content.Server.Station.Systems;
-using Content.Shared.Disease;
-using Content.Shared.DoAfter;
-using Content.Shared.Examine;
-using Content.Shared.Hands.Components;
-using Content.Shared.IdentityManagement;
-using Content.Shared.Interaction;
-using Content.Shared.Inventory;
-using Content.Shared.Swab;
-using Content.Shared.Tools.Components;
-using Robust.Shared.Audio;
-using Robust.Shared.Player;
-using Robust.Shared.Random;
-using Robust.Shared.Utility;
-
-namespace Content.Server.Disease
-{
- /// <summary>
- /// Everything that's about disease diangosis and machines is in here
- /// </summary>
- public sealed class DiseaseDiagnosisSystem : EntitySystem
- {
- [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly InventorySystem _inventorySystem = default!;
- [Dependency] private readonly PaperSystem _paperSystem = default!;
- [Dependency] private readonly StationSystem _stationSystem = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<DiseaseSwabComponent, AfterInteractEvent>(OnAfterInteract);
- SubscribeLocalEvent<DiseaseSwabComponent, ExaminedEvent>(OnExamined);
- SubscribeLocalEvent<DiseaseDiagnoserComponent, AfterInteractUsingEvent>(OnAfterInteractUsing);
- SubscribeLocalEvent<DiseaseVaccineCreatorComponent, AfterInteractUsingEvent>(OnAfterInteractUsingVaccine);
- // Visuals
- SubscribeLocalEvent<DiseaseMachineComponent, PowerChangedEvent>(OnPowerChanged);
- // Private Events
- SubscribeLocalEvent<DiseaseDiagnoserComponent, DiseaseMachineFinishedEvent>(OnDiagnoserFinished);
- SubscribeLocalEvent<DiseaseVaccineCreatorComponent, DiseaseMachineFinishedEvent>(OnVaccinatorFinished);
- SubscribeLocalEvent<DiseaseSwabComponent, DiseaseSwabDoAfterEvent>(OnSwabDoAfter);
- }
-
- private Queue<EntityUid> AddQueue = new();
- private Queue<EntityUid> RemoveQueue = new();
-
- /// <summary>
- /// This handles running disease machines
- /// to handle their delay and visuals.
- /// </summary>
- public override void Update(float frameTime)
- {
- foreach (var uid in AddQueue)
- {
- EnsureComp<DiseaseMachineRunningComponent>(uid);
- }
-
- AddQueue.Clear();
- foreach (var uid in RemoveQueue)
- {
- RemComp<DiseaseMachineRunningComponent>(uid);
- }
-
- RemoveQueue.Clear();
-
- foreach (var (_, diseaseMachine) in EntityQuery<DiseaseMachineRunningComponent, DiseaseMachineComponent>())
- {
- diseaseMachine.Accumulator += frameTime;
-
- while (diseaseMachine.Accumulator >= diseaseMachine.Delay)
- {
- diseaseMachine.Accumulator -= diseaseMachine.Delay;
- var ev = new DiseaseMachineFinishedEvent(diseaseMachine);
- RaiseLocalEvent(diseaseMachine.Owner, ev);
- RemoveQueue.Enqueue(diseaseMachine.Owner);
- }
- }
- }
-
- ///
- /// Event Handlers
- ///
-
- /// <summary>
- /// This handles using swabs on other people
- /// and checks that the swab isn't already used
- /// and the other person's mouth is accessible
- /// and then adds a random disease from that person
- /// to the swab if they have any
- /// </summary>
- private void OnAfterInteract(EntityUid uid, DiseaseSwabComponent swab, AfterInteractEvent args)
- {
- if (args.Target == null || !args.CanReach || !HasComp<DiseaseCarrierComponent>(args.Target))
- return;
-
- if (swab.Used)
- {
- _popupSystem.PopupEntity(Loc.GetString("swab-already-used"), args.User, args.User);
- return;
- }
-
- if (_inventorySystem.TryGetSlotEntity(args.Target.Value, "mask", out var maskUid) &&
- EntityManager.TryGetComponent<IngestionBlockerComponent>(maskUid, out var blocker) &&
- blocker.Enabled)
- {
- _popupSystem.PopupEntity(Loc.GetString("swab-mask-blocked", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("mask", maskUid)), args.User, args.User);
- return;
- }
-
- _doAfterSystem.TryStartDoAfter(new DoAfterArgs(args.User, swab.SwabDelay, new DiseaseSwabDoAfterEvent(), uid, target: args.Target, used: uid)
- {
- BreakOnTargetMove = true,
- BreakOnUserMove = true,
- NeedHand = true
- });
- }
-
- /// <summary>
- /// This handles the disease diagnoser machine up
- /// until it's turned on. It has some slight
- /// differences in checks from the vaccinator.
- /// </summary>
- private void OnAfterInteractUsing(EntityUid uid, DiseaseDiagnoserComponent component, AfterInteractUsingEvent args)
- {
- var machine = Comp<DiseaseMachineComponent>(uid);
- if (args.Handled || !args.CanReach)
- return;
-
- if (HasComp<DiseaseMachineRunningComponent>(uid) || !this.IsPowered(uid, EntityManager))
- return;
-
- if (!HasComp<HandsComponent>(args.User) || HasComp<ToolComponent>(args.Used)) // Don't want to accidentally breach wrenching or whatever
- return;
-
- if (!TryComp<DiseaseSwabComponent>(args.Used, out var swab))
- {
- _popupSystem.PopupEntity(Loc.GetString("diagnoser-cant-use-swab", ("machine", uid), ("swab", args.Used)), uid, args.User);
- return;
- }
- _popupSystem.PopupEntity(Loc.GetString("machine-insert-item", ("machine", uid), ("item", args.Used), ("user", args.User)), uid, args.User);
-
-
- machine.Disease = swab.Disease;
- EntityManager.DeleteEntity(args.Used);
-
- AddQueue.Enqueue(uid);
- UpdateAppearance(uid, true, true);
- SoundSystem.Play("/Audio/Machines/diagnoser_printing.ogg", Filter.Pvs(uid), uid);
- }
-
- /// <summary>
- /// This handles the vaccinator machine up
- /// until it's turned on. It has some slight
- /// differences in checks from the diagnoser.
- /// </summary>
- private void OnAfterInteractUsingVaccine(EntityUid uid, DiseaseVaccineCreatorComponent component, AfterInteractUsingEvent args)
- {
- if (args.Handled || !args.CanReach)
- return;
-
- if (HasComp<DiseaseMachineRunningComponent>(uid) || !this.IsPowered(uid, EntityManager))
- return;
-
- if (!HasComp<HandsComponent>(args.User) || HasComp<ToolComponent>(args.Used)) //This check ensures tools don't break without yaml ordering jank
- return;
-
- if (!TryComp<DiseaseSwabComponent>(args.Used, out var swab) || swab.Disease == null || !swab.Disease.Infectious)
- {
- _popupSystem.PopupEntity(Loc.GetString("diagnoser-cant-use-swab", ("machine", uid), ("swab", args.Used)), uid, args.User);
- return;
- }
- _popupSystem.PopupEntity(Loc.GetString("machine-insert-item", ("machine", uid), ("item", args.Used), ("user", args.User)), uid, args.User);
- var machine = Comp<DiseaseMachineComponent>(uid);
- machine.Disease = swab.Disease;
- EntityManager.DeleteEntity(args.Used);
-
- AddQueue.Enqueue(uid);
- UpdateAppearance(uid, true, true);
- SoundSystem.Play("/Audio/Machines/vaccinator_running.ogg", Filter.Pvs(uid), uid);
- }
-
- /// <summary>
- /// This handles swab examination text
- /// so you can tell if they are used or not.
- /// </summary>
- private void OnExamined(EntityUid uid, DiseaseSwabComponent swab, ExaminedEvent args)
- {
- if (args.IsInDetailsRange)
- {
- if (swab.Used)
- args.PushMarkup(Loc.GetString("swab-used"));
- else
- args.PushMarkup(Loc.GetString("swab-unused"));
- }
- }
-
- ///
- /// Helper functions
- ///
-
- /// <summary>
- /// This assembles a disease report
- /// With its basic details and
- /// specific cures (i.e. not spaceacillin).
- /// The cure resist field tells you how
- /// effective spaceacillin etc will be.
- /// </summary>
- private FormattedMessage AssembleDiseaseReport(DiseasePrototype disease)
- {
- FormattedMessage report = new();
- var diseaseName = Loc.GetString(disease.Name);
- report.AddMarkup(Loc.GetString("diagnoser-disease-report-name", ("disease", diseaseName)));
- report.PushNewline();
-
- if (disease.Infectious)
- {
- report.AddMarkup(Loc.GetString("diagnoser-disease-report-infectious"));
- report.PushNewline();
- } else
- {
- report.AddMarkup(Loc.GetString("diagnoser-disease-report-not-infectious"));
- report.PushNewline();
- }
- string cureResistLine = string.Empty;
- cureResistLine += disease.CureResist switch
- {
- < 0f => Loc.GetString("diagnoser-disease-report-cureresist-none"),
- <= 0.05f => Loc.GetString("diagnoser-disease-report-cureresist-low"),
- <= 0.14f => Loc.GetString("diagnoser-disease-report-cureresist-medium"),
- _ => Loc.GetString("diagnoser-disease-report-cureresist-high")
- };
- report.AddMarkup(cureResistLine);
- report.PushNewline();
-
- // Add Cures
- if (disease.Cures.Count == 0)
- {
- report.AddMarkup(Loc.GetString("diagnoser-no-cures"));
- }
- else
- {
- report.PushNewline();
- report.AddMarkup(Loc.GetString("diagnoser-cure-has"));
- report.PushNewline();
-
- foreach (var cure in disease.Cures)
- {
- report.AddMarkup(cure.CureText());
- report.PushNewline();
- }
- }
-
- return report;
- }
-
- public bool ServerHasDisease(DiseaseServerComponent server, DiseasePrototype disease)
- {
- bool has = false;
- foreach (var serverDisease in server.Diseases)
- {
- if (serverDisease.ID == disease.ID)
- has = true;
- }
- return has;
- }
- ///
- /// Appearance stuff
- ///
-
- /// <summary>
- /// Appearance helper function to
- /// set the component's power and running states.
- /// </summary>
- private void UpdateAppearance(EntityUid uid, bool isOn, bool isRunning)
- {
- if (!TryComp<AppearanceComponent>(uid, out var appearance))
- return;
-
- _appearance.SetData(uid, DiseaseMachineVisuals.IsOn, isOn, appearance);
- _appearance.SetData(uid, DiseaseMachineVisuals.IsRunning, isRunning, appearance);
- }
- /// <summary>
- /// Makes sure the machine is visually off/on.
- /// </summary>
- private void OnPowerChanged(EntityUid uid, DiseaseMachineComponent component, ref PowerChangedEvent args)
- {
- UpdateAppearance(uid, args.Powered, false);
- }
-
- /// <summary>
- /// Copies a disease prototype to the swab
- /// after the doafter completes.
- /// </summary>
- private void OnSwabDoAfter(EntityUid uid, DiseaseSwabComponent component, DoAfterEvent args)
- {
- if (args.Handled || args.Cancelled || !TryComp<DiseaseCarrierComponent>(args.Args.Target, out var carrier) || !TryComp<DiseaseSwabComponent>(args.Args.Used, out var swab))
- return;
-
- swab.Used = true;
- _popupSystem.PopupEntity(Loc.GetString("swab-swabbed", ("target", Identity.Entity(args.Args.Target.Value, EntityManager))), args.Args.Target.Value, args.Args.User);
-
- if (swab.Disease != null || carrier.Diseases.Count == 0)
- return;
-
- swab.Disease = _random.Pick(carrier.Diseases);
- }
-
-
- /// <summary>
- /// Prints a diagnostic report with its findings.
- /// Also cancels the animation.
- /// </summary>
- private void OnDiagnoserFinished(EntityUid uid, DiseaseDiagnoserComponent component, DiseaseMachineFinishedEvent args)
- {
- var isPowered = this.IsPowered(uid, EntityManager);
- UpdateAppearance(uid, isPowered, false);
- // spawn a piece of paper.
- var printed = Spawn(args.Machine.MachineOutput, Transform(uid).Coordinates);
-
- if (!TryComp<PaperComponent>(printed, out var paper))
- return;
-
- string reportTitle;
- FormattedMessage contents = new();
- if (args.Machine.Disease != null)
- {
- var diseaseName = Loc.GetString(args.Machine.Disease.Name);
- reportTitle = Loc.GetString("diagnoser-disease-report", ("disease", diseaseName));
- contents = AssembleDiseaseReport(args.Machine.Disease);
-
- var known = false;
-
- foreach (var server in EntityQuery<DiseaseServerComponent>(true))
- {
- if (_stationSystem.GetOwningStation(server.Owner) != _stationSystem.GetOwningStation(uid))
- continue;
-
- if (ServerHasDisease(server, args.Machine.Disease))
- {
- known = true;
- }
- else
- {
- server.Diseases.Add(args.Machine.Disease);
- }
- }
-
- if (!known)
- {
- Spawn("ResearchDisk5000", Transform(uid).Coordinates);
- }
- }
- else
- {
- reportTitle = Loc.GetString("diagnoser-disease-report-none");
- contents.AddMarkup(Loc.GetString("diagnoser-disease-report-none-contents"));
- }
- MetaData(printed).EntityName = reportTitle;
-
- _paperSystem.SetContent(printed, contents.ToMarkup(), paper);
- }
-
- /// <summary>
- /// Prints a vaccine that will vaccinate
- /// against the disease on the inserted swab.
- /// </summary>
- private void OnVaccinatorFinished(EntityUid uid, DiseaseVaccineCreatorComponent component, DiseaseMachineFinishedEvent args)
- {
- UpdateAppearance(uid, this.IsPowered(uid, EntityManager), false);
-
- // spawn a vaccine
- var vaxx = Spawn(args.Machine.MachineOutput, Transform(uid).Coordinates);
-
- if (!TryComp<DiseaseVaccineComponent>(vaxx, out var vaxxComp))
- return;
-
- vaxxComp.Disease = args.Machine.Disease;
- }
-
- /// <summary>
- /// Fires when a disease machine is done
- /// with its production delay and ready to
- /// create a report or vaccine
- /// </summary>
- private sealed class DiseaseMachineFinishedEvent : EntityEventArgs
- {
- public DiseaseMachineComponent Machine {get;}
- public DiseaseMachineFinishedEvent(DiseaseMachineComponent machine)
- {
- Machine = machine;
- }
- }
- }
-}
-
+++ /dev/null
-using Content.Server.Body.Systems;
-using Content.Server.Chat.Systems;
-using Content.Server.Disease.Components;
-using Content.Server.Nutrition.EntitySystems;
-using Content.Server.Popups;
-using Content.Shared.Clothing.Components;
-using Content.Shared.Disease;
-using Content.Shared.Disease.Components;
-using Content.Shared.Disease.Events;
-using Content.Shared.DoAfter;
-using Content.Shared.Examine;
-using Content.Shared.IdentityManagement;
-using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
-using Content.Shared.Inventory;
-using Content.Shared.Inventory.Events;
-using Content.Shared.Mobs.Components;
-using Content.Shared.Mobs.Systems;
-using Content.Shared.Rejuvenate;
-using Robust.Shared.Audio;
-using Robust.Server.GameObjects;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Random;
-using Robust.Shared.Serialization.Manager;
-using Robust.Shared.Utility;
-
-namespace Content.Server.Disease
-{
-
- /// <summary>
- /// Handles disease propagation & curing
- /// </summary>
- public sealed class DiseaseSystem : EntitySystem
- {
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly ISerializationManager _serializationManager = default!;
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
- [Dependency] private readonly EntityLookupSystem _lookup = default!;
- [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
- [Dependency] private readonly InventorySystem _inventorySystem = default!;
- [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
- [Dependency] private readonly ChatSystem _chatSystem = default!;
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<DiseaseCarrierComponent, ComponentInit>(OnInit);
- SubscribeLocalEvent<DiseaseCarrierComponent, CureDiseaseAttemptEvent>(OnTryCureDisease);
- SubscribeLocalEvent<DiseaseCarrierComponent, RejuvenateEvent>(OnRejuvenate);
- SubscribeLocalEvent<DiseasedComponent, ContactInteractionEvent>(OnContactInteraction);
- SubscribeLocalEvent<DiseasedComponent, EntitySpokeEvent>(OnEntitySpeak);
- SubscribeLocalEvent<DiseaseProtectionComponent, GotEquippedEvent>(OnEquipped);
- SubscribeLocalEvent<DiseaseProtectionComponent, GotUnequippedEvent>(OnUnequipped);
- SubscribeLocalEvent<DiseaseVaccineComponent, AfterInteractEvent>(OnAfterInteract);
- SubscribeLocalEvent<DiseaseVaccineComponent, ExaminedEvent>(OnExamined);
- // Handling stuff from other systems
- SubscribeLocalEvent<DiseaseCarrierComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
- // Private events stuff
- SubscribeLocalEvent<DiseaseVaccineComponent, VaccineDoAfterEvent>(OnDoAfter);
- }
-
- private Queue<EntityUid> AddQueue = new();
- private Queue<(DiseaseCarrierComponent carrier, DiseasePrototype disease)> CureQueue = new();
-
- /// <summary>
- /// First, adds or removes diseased component from the queues and clears them.
- /// Then, iterates over every diseased component to check for their effects
- /// and cures
- /// </summary>
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
- foreach (var entity in AddQueue)
- {
- EnsureComp<DiseasedComponent>(entity);
- }
-
- AddQueue.Clear();
-
- foreach (var tuple in CureQueue)
- {
- if (tuple.carrier.Diseases.Count == 1) //This is reliable unlike testing Count == 0 right after removal for reasons I don't quite get
- RemComp<DiseasedComponent>(tuple.carrier.Owner);
- tuple.carrier.PastDiseases.Add(tuple.disease);
- tuple.carrier.Diseases.Remove(tuple.disease);
- }
- CureQueue.Clear();
-
- foreach (var (_, carrierComp, mobState) in EntityQuery<DiseasedComponent, DiseaseCarrierComponent, MobStateComponent>())
- {
- DebugTools.Assert(carrierComp.Diseases.Count > 0);
-
- if (_mobStateSystem.IsDead(mobState.Owner, mobState))
- {
- if (_random.Prob(0.005f * frameTime)) //Mean time to remove is 200 seconds per disease
- CureDisease(carrierComp, _random.Pick(carrierComp.Diseases));
-
- continue;
- }
-
- for (var i = 0; i < carrierComp.Diseases.Count; i++) //this is a for-loop so that it doesn't break when new diseases are added
- {
- var disease = carrierComp.Diseases[i];
- disease.Accumulator += frameTime;
- disease.TotalAccumulator += frameTime;
-
- if (disease.Accumulator < disease.TickTime) continue;
-
- // if the disease is on the silent disease list, don't do effects
- var doEffects = carrierComp.CarrierDiseases?.Contains(disease.ID) != true;
- var args = new DiseaseEffectArgs(carrierComp.Owner, disease, EntityManager);
- disease.Accumulator -= disease.TickTime;
-
- int stage = 0; //defaults to stage 0 because you should always have one
- float lastThreshold = 0;
- for (var j = 0; j < disease.Stages.Count; j++)
- {
- if (disease.TotalAccumulator >= disease.Stages[j] &&
- disease.Stages[j] > lastThreshold)
- {
- lastThreshold = disease.Stages[j];
- stage = j;
- }
- }
-
- foreach (var cure in disease.Cures)
- {
- if (cure.Stages.AsSpan().Contains(stage) && cure.Cure(args))
- CureDisease(carrierComp, disease);
- }
-
- if (doEffects)
- {
- foreach (var effect in disease.Effects)
- {
- if (effect.Stages.AsSpan().Contains(stage) && _random.Prob(effect.Probability))
- effect.Effect(args);
- }
- }
- }
- }
- }
-
- ///
- /// Event Handlers
- ///
-
- /// <summary>
- /// Fill in the natural immunities of this entity.
- /// </summary>
- private void OnInit(EntityUid uid, DiseaseCarrierComponent component, ComponentInit args)
- {
- if (component.NaturalImmunities == null || component.NaturalImmunities.Count == 0)
- return;
-
- foreach (var immunity in component.NaturalImmunities)
- {
- if (_prototypeManager.TryIndex<DiseasePrototype>(immunity, out var disease))
- component.PastDiseases.Add(disease);
- else
- {
- Logger.Error("Failed to index disease prototype + " + immunity + " for " + uid);
- }
- }
- }
-
- /// <summary>
- /// Used when something is trying to cure ANY disease on the target,
- /// not for special disease interactions. Randomly
- /// tries to cure every disease on the target.
- /// </summary>
- private void OnTryCureDisease(EntityUid uid, DiseaseCarrierComponent component, CureDiseaseAttemptEvent args)
- {
- foreach (var disease in component.Diseases)
- {
- var cureProb = ((args.CureChance / component.Diseases.Count) - disease.CureResist);
- if (cureProb < 0)
- return;
- if (cureProb > 1)
- {
- CureDisease(component, disease);
- return;
- }
- if (_random.Prob(cureProb))
- {
- CureDisease(component, disease);
- return;
- }
- }
- }
-
- private void OnRejuvenate(EntityUid uid, DiseaseCarrierComponent component, RejuvenateEvent args)
- {
- CureAllDiseases(uid, component);
- }
-
- /// <summary>
- /// Called when a component with disease protection
- /// is equipped so it can be added to the person's
- /// total disease resistance
- /// </summary>
- private void OnEquipped(EntityUid uid, DiseaseProtectionComponent component, GotEquippedEvent args)
- {
- // This only works on clothing
- if (!TryComp<ClothingComponent>(uid, out var clothing))
- return;
- // Is the clothing in its actual slot?
- if (!clothing.Slots.HasFlag(args.SlotFlags))
- return;
- // Give the user the component's disease resist
- if(TryComp<DiseaseCarrierComponent>(args.Equipee, out var carrier))
- carrier.DiseaseResist += component.Protection;
- // Set the component to active to the unequip check isn't CBT
- component.IsActive = true;
- }
-
- /// <summary>
- /// Called when a component with disease protection
- /// is unequipped so it can be removed from the person's
- /// total disease resistance
- /// </summary>
- private void OnUnequipped(EntityUid uid, DiseaseProtectionComponent component, GotUnequippedEvent args)
- {
- // Only undo the resistance if it was affecting the user
- if (!component.IsActive)
- return;
- if(TryComp<DiseaseCarrierComponent>(args.Equipee, out var carrier))
- carrier.DiseaseResist -= component.Protection;
- component.IsActive = false;
- }
-
- /// <summary>
- /// Called when it's already decided a disease will be cured
- /// so it can be safely queued up to be removed from the target
- /// and added to past disease history (for immunity)
- /// </summary>
- private void CureDisease(DiseaseCarrierComponent carrier, DiseasePrototype disease)
- {
- var CureTuple = (carrier, disease);
- CureQueue.Enqueue(CureTuple);
- _popupSystem.PopupEntity(Loc.GetString("disease-cured"), carrier.Owner, carrier.Owner);
- }
-
- public void CureAllDiseases(EntityUid uid, DiseaseCarrierComponent? carrier = null)
- {
- if (!Resolve(uid, ref carrier))
- return;
-
- foreach (var disease in carrier.Diseases)
- {
- CureDisease(carrier, disease);
- }
- }
-
- /// <summary>
- /// When a diseased person interacts with something, check infection.
- /// </summary>
- private void OnContactInteraction(EntityUid uid, DiseasedComponent component, ContactInteractionEvent args)
- {
- InteractWithDiseased(uid, args.Other);
- }
-
- private void OnEntitySpeak(EntityUid uid, DiseasedComponent component, EntitySpokeEvent args)
- {
- if (TryComp<DiseaseCarrierComponent>(uid, out var carrier))
- {
- SneezeCough(uid, _random.Pick(carrier.Diseases), string.Empty);
- }
- }
-
- /// <summary>
- /// Called when a vaccine is used on someone
- /// to handle the vaccination doafter
- /// </summary>
- private void OnAfterInteract(EntityUid uid, DiseaseVaccineComponent vaxx, AfterInteractEvent args)
- {
- if (args.Target == null || !args.CanReach || args.Handled)
- return;
-
- args.Handled = true;
-
- if (vaxx.Used)
- {
- _popupSystem.PopupEntity(Loc.GetString("vaxx-already-used"), args.User, args.User);
- return;
- }
-
- _doAfterSystem.TryStartDoAfter(new DoAfterArgs(args.User, vaxx.InjectDelay, new VaccineDoAfterEvent(), uid, target: args.Target, used: uid)
- {
- BreakOnTargetMove = true,
- BreakOnUserMove = true,
- NeedHand = true
- });
- }
-
- /// <summary>
- /// Called when a vaccine is examined.
- /// Currently doesn't do much because
- /// vaccines don't have unique art with a seperate
- /// state visualizer.
- /// </summary>
- private void OnExamined(EntityUid uid, DiseaseVaccineComponent vaxx, ExaminedEvent args)
- {
- if (args.IsInDetailsRange)
- {
- if (vaxx.Used)
- args.PushMarkup(Loc.GetString("vaxx-used"));
- else
- args.PushMarkup(Loc.GetString("vaxx-unused"));
- }
- }
-
-
- private void OnApplyMetabolicMultiplier(EntityUid uid, DiseaseCarrierComponent component, ApplyMetabolicMultiplierEvent args)
- {
- if (args.Apply)
- {
- foreach (var disease in component.Diseases)
- {
- disease.TickTime *= args.Multiplier;
- return;
- }
- }
- foreach (var disease in component.Diseases)
- {
- disease.TickTime /= args.Multiplier;
- if (disease.Accumulator >= disease.TickTime)
- disease.Accumulator = disease.TickTime;
- }
- }
-
- ///
- /// Helper functions
- ///
-
- /// <summary>
- /// Tries to infect anyone that
- /// interacts with a diseased person or body
- /// </summary>
- private void InteractWithDiseased(EntityUid diseased, EntityUid target, DiseaseCarrierComponent? diseasedCarrier = null)
- {
- if (!Resolve(diseased, ref diseasedCarrier, false) ||
- diseasedCarrier.Diseases.Count == 0 ||
- !TryComp<DiseaseCarrierComponent>(target, out var carrier))
- return;
-
- var disease = _random.Pick(diseasedCarrier.Diseases);
- TryInfect(carrier, disease, 0.4f);
- }
-
- /// <summary>
- /// Adds a disease to a target
- /// if it's not already in their current
- /// or past diseases. If you want this
- /// to not be guaranteed you are looking
- /// for TryInfect.
- /// </summary>
- public void TryAddDisease(EntityUid host, DiseasePrototype addedDisease, DiseaseCarrierComponent? target = null)
- {
- if (!Resolve(host, ref target, false))
- return;
-
- foreach (var disease in target.AllDiseases)
- {
- if (disease.ID == addedDisease?.ID) //ID because of the way protoypes work
- return;
- }
-
- var freshDisease = _serializationManager.CreateCopy(addedDisease, notNullableOverride: true);
-
- if (freshDisease == null) return;
-
- target.Diseases.Add(freshDisease);
- AddQueue.Enqueue(target.Owner);
- }
-
- public void TryAddDisease(EntityUid host, string? addedDisease, DiseaseCarrierComponent? target = null)
- {
- if (addedDisease == null || !_prototypeManager.TryIndex<DiseasePrototype>(addedDisease, out var added))
- return;
-
- TryAddDisease(host, added, target);
- }
-
- /// <summary>
- /// Pits the infection chance against the
- /// person's disease resistance and
- /// rolls the dice to see if they get
- /// the disease.
- /// </summary>
- /// <param name="carrier">The target of the disease</param>
- /// <param name="disease">The disease to apply</param>
- /// <param name="chance">% chance of the disease being applied, before considering resistance</param>
- /// <param name="forced">Bypass the disease's infectious trait.</param>
- public void TryInfect(DiseaseCarrierComponent carrier, DiseasePrototype? disease, float chance = 0.7f, bool forced = false)
- {
- if(disease == null || !forced && !disease.Infectious)
- return;
- var infectionChance = chance - carrier.DiseaseResist;
- if (infectionChance <= 0)
- return;
- if (_random.Prob(infectionChance))
- TryAddDisease(carrier.Owner, disease, carrier);
- }
-
- public void TryInfect(DiseaseCarrierComponent carrier, string? disease, float chance = 0.7f, bool forced = false)
- {
- if (disease == null || !_prototypeManager.TryIndex<DiseasePrototype>(disease, out var d))
- return;
-
- TryInfect(carrier, d, chance, forced);
- }
-
- /// <summary>
- /// Raises an event for systems to cancel the snough if needed
- /// Plays a sneeze/cough sound and popup if applicable
- /// and then tries to infect anyone in range
- /// if the snougher is not wearing a mask.
- /// </summary>
- public bool SneezeCough(EntityUid uid, DiseasePrototype? disease, string emoteId, bool airTransmit = true, TransformComponent? xform = null)
- {
- if (!Resolve(uid, ref xform)) return false;
-
- if (_mobStateSystem.IsDead(uid)) return false;
-
- var attemptSneezeCoughEvent = new AttemptSneezeCoughEvent(uid, emoteId);
- RaiseLocalEvent(uid, ref attemptSneezeCoughEvent);
- if (attemptSneezeCoughEvent.Cancelled) return false;
-
- _chatSystem.TryEmoteWithChat(uid, emoteId);
-
- if (disease is not { Infectious: true } || !airTransmit)
- return true;
-
- if (_inventorySystem.TryGetSlotEntity(uid, "mask", out var maskUid) &&
- EntityManager.TryGetComponent<IngestionBlockerComponent>(maskUid, out var blocker) &&
- blocker.Enabled)
- return true;
-
- var carrierQuery = GetEntityQuery<DiseaseCarrierComponent>();
-
- foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, 2f))
- {
- if (!carrierQuery.TryGetComponent(entity, out var carrier) ||
- !_interactionSystem.InRangeUnobstructed(uid, entity)) continue;
-
- TryInfect(carrier, disease, 0.3f);
- }
- return true;
- }
-
- /// <summary>
- /// Adds a disease to the carrier's
- /// past diseases to give them immunity
- /// IF they don't already have the disease.
- /// </summary>
- public void Vaccinate(DiseaseCarrierComponent carrier, DiseasePrototype disease)
- {
- foreach (var currentDisease in carrier.Diseases)
- {
- if (currentDisease.ID == disease.ID) //ID because of the way protoypes work
- return;
- }
- carrier.PastDiseases.Add(disease);
- }
-
- private void OnDoAfter(EntityUid uid, DiseaseVaccineComponent component, DoAfterEvent args)
- {
- if (args.Handled || args.Cancelled || !TryComp<DiseaseCarrierComponent>(args.Args.Target, out var carrier) || component.Disease == null)
- return;
-
- Vaccinate(carrier, component.Disease);
- EntityManager.DeleteEntity(uid);
- args.Handled = true;
- }
- }
-
- /// <summary>
- /// This event is fired by chems
- /// and other brute-force rather than
- /// specific cures. It will roll the dice to attempt
- /// to cure each disease on the target
- /// </summary>
- public sealed class CureDiseaseAttemptEvent : EntityEventArgs
- {
- public float CureChance { get; }
- public CureDiseaseAttemptEvent(float cureChance)
- {
- CureChance = cureChance;
- }
- }
-
- /// <summary>
- /// Controls whether the snough is a sneeze, cough
- /// or neither. If none, will not create
- /// a popup. Mostly used for talking
- /// </summary>
- public enum SneezeCoughType
- {
- Sneeze,
- Cough,
- None
- }
-}
+++ /dev/null
-using JetBrains.Annotations;
-using Content.Shared.Disease;
-
-namespace Content.Server.Disease.Effects
-{
- /// <summary>
- /// Adds a component to the diseased entity
- /// </summary>
- [UsedImplicitly]
- public sealed class DiseaseAddComponent : DiseaseEffect
- {
- /// <summary>
- /// The component that is added at the end of build up
- /// </summary>
- [DataField("comp")]
- public string? Comp = null;
-
- public override void Effect(DiseaseEffectArgs args)
- {
- if (Comp == null)
- return;
-
- EntityUid uid = args.DiseasedEntity;
- var newComponent = (Component) IoCManager.Resolve<IComponentFactory>().GetComponent(Comp);
- newComponent.Owner = uid;
- if (!args.EntityManager.HasComponent(uid, newComponent.GetType()))
- args.EntityManager.AddComponent(uid, newComponent);
- }
- }
-}
+++ /dev/null
-using Content.Server.Chemistry.EntitySystems;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.FixedPoint;
-using JetBrains.Annotations;
-using Content.Server.Body.Components;
-using Content.Shared.Disease;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Disease.Effects
-{
- /// <summary>
- /// Adds or removes reagents from the
- /// host's chemstream.
- /// </summary>
- [UsedImplicitly]
- public sealed class DiseaseAdjustReagent : DiseaseEffect
- {
- /// <summary>
- /// The reagent ID to add or remove.
- /// </summary>
- [DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>))]
- public string? Reagent = null;
-
- [DataField("amount", required: true)]
- public FixedPoint2 Amount = default!;
-
- public override void Effect(DiseaseEffectArgs args)
- {
- if (!args.EntityManager.TryGetComponent<BloodstreamComponent>(args.DiseasedEntity, out var bloodstream))
- return;
-
- var stream = bloodstream.ChemicalSolution;
- if (stream == null)
- return;
-
- var solutionSys = args.EntityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
- if (Reagent == null)
- return;
-
- if (Amount < 0 && stream.ContainsReagent(Reagent))
- solutionSys.TryRemoveReagent(args.DiseasedEntity, stream, Reagent, -Amount);
-
- if (Amount > 0)
- solutionSys.TryAddReagent(args.DiseasedEntity, stream, Reagent, Amount, out _);
- }
- }
-}
+++ /dev/null
-using Content.Shared.Disease;
-using Content.Shared.StatusEffect;
-using JetBrains.Annotations;
-
-namespace Content.Server.Disease.Effects
-{
- /// <summary>
- /// Adds a generic status effect to the entity.
- /// Differs from the chem version in its defaults
- /// to better facilitate adding components that
- /// last the length of the disease.
- /// </summary>
- [UsedImplicitly]
- public sealed class DiseaseGenericStatusEffect : DiseaseEffect
- {
- /// <summary>
- /// The status effect key
- /// Prevents other components from being with the same key
- /// </summary>
- [DataField("key", required: true)]
- public string Key = default!;
- /// <summary>
- /// The component to add
- /// </summary>
- [DataField("component")]
- public string Component = String.Empty;
-
- [DataField("time")]
- public float Time = 1.01f; /// I'm afraid if this was exact the key could get stolen by another thing
-
- /// <remarks>
- /// true - refresh status effect time, false - accumulate status effect time
- /// </remarks>
- [DataField("refresh")]
- public bool Refresh = false;
-
- /// <summary>
- /// Should this effect add the status effect, remove time from it, or set its cooldown?
- /// </summary>
- [DataField("type")]
- public StatusEffectDiseaseType Type = StatusEffectDiseaseType.Add;
-
- public override void Effect(DiseaseEffectArgs args)
- {
- var statusSys = EntitySystem.Get<StatusEffectsSystem>();
- if (Type == StatusEffectDiseaseType.Add && Component != String.Empty)
- {
- statusSys.TryAddStatusEffect(args.DiseasedEntity, Key, TimeSpan.FromSeconds(Time), Refresh, Component);
- }
- else if (Type == StatusEffectDiseaseType.Remove)
- {
- statusSys.TryRemoveTime(args.DiseasedEntity, Key, TimeSpan.FromSeconds(Time));
- }
- else if (Type == StatusEffectDiseaseType.Set)
- {
- statusSys.TrySetTime(args.DiseasedEntity, Key, TimeSpan.FromSeconds(Time));
- }
- }
- }
- /// See status effects for how these work
- public enum StatusEffectDiseaseType
- {
- Add,
- Remove,
- Set
- }
-}
+++ /dev/null
-using Content.Shared.Disease;
-using Content.Shared.Damage;
-using JetBrains.Annotations;
-
-namespace Content.Server.Disease.Effects
-{
- /// <summary>
- /// Deals or heals damage to the host
- /// </summary>
- [UsedImplicitly]
- public sealed class DiseaseHealthChange : DiseaseEffect
- {
- [DataField("damage", required: true)]
- [ViewVariables(VVAccess.ReadWrite)]
- public DamageSpecifier Damage = default!;
- public override void Effect(DiseaseEffectArgs args)
- {
- EntitySystem.Get<DamageableSystem>().TryChangeDamage(args.DiseasedEntity, Damage, true, false);
- }
- }
-}
+++ /dev/null
-using Content.Shared.Chat.Prototypes;
-using Content.Shared.Disease;
-using JetBrains.Annotations;
-using Robust.Shared.Audio;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Disease
-{
- /// <summary>
- /// Makes the diseased honk.
- /// or neither.
- /// </summary>
- [UsedImplicitly]
- public sealed class DiseaseHonk : DiseaseEffect
- {
- /// <summary>
- /// Message to play when honking.
- /// </summary>
- [DataField("honkMessage")]
- public string HonkMessage = "disease-honk";
-
- /// <summary>
- /// Emote to play when honking.
- /// </summary>
- [DataField("emote", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
- public string EmoteId = String.Empty;
-
- /// <summary>
- /// Whether to spread the disease through the air.
- /// </summary>
- [DataField("airTransmit")]
- public bool AirTransmit = false;
-
- public override void Effect(DiseaseEffectArgs args)
- {
- EntitySystem.Get<DiseaseSystem>().SneezeCough(args.DiseasedEntity, args.Disease, EmoteId, AirTransmit);
- }
- }
-}
+++ /dev/null
-using Content.Server.Polymorph.Systems;
-using Content.Shared.Audio;
-using Content.Shared.Disease;
-using Content.Shared.Polymorph;
-using Content.Shared.Popups;
-using JetBrains.Annotations;
-using Robust.Shared.Audio;
-using Robust.Shared.Player;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Disease.Effects
-{
- [UsedImplicitly]
- public sealed class DiseasePolymorph : DiseaseEffect
- {
- [DataField("polymorphId", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<PolymorphPrototype>))]
- [ViewVariables(VVAccess.ReadWrite)]
- public readonly string PolymorphId = default!;
-
- [DataField("polymorphSound")]
- [ViewVariables(VVAccess.ReadWrite)]
- public SoundSpecifier? PolymorphSound;
-
- [DataField("polymorphMessage")]
- [ViewVariables(VVAccess.ReadWrite)]
- public string? PolymorphMessage;
-
- public override void Effect(DiseaseEffectArgs args)
- {
- EntityUid? polyUid = EntitySystem.Get<PolymorphSystem>().PolymorphEntity(args.DiseasedEntity, PolymorphId);
-
- if (PolymorphSound != null && polyUid != null)
- SoundSystem.Play(PolymorphSound.GetSound(), Filter.Pvs(polyUid.Value), polyUid.Value, AudioHelpers.WithVariation(0.2f));
-
- if (PolymorphMessage != null && polyUid != null)
- EntitySystem.Get<SharedPopupSystem>().PopupEntity(Loc.GetString(PolymorphMessage), polyUid.Value, polyUid.Value, PopupType.Large);
- }
- }
-}
+++ /dev/null
-using Content.Shared.Disease;
-using Content.Shared.Popups;
-using Robust.Shared.Player;
-using JetBrains.Annotations;
-using Content.Shared.IdentityManagement;
-
-namespace Content.Server.Disease.Effects
-{
- /// <summary>
- /// Plays a popup on the host's transform.
- /// Supports passing the host's entity metadata
- /// in PVS ones with {$person}
- /// </summary>
- [UsedImplicitly]
- public sealed class DiseasePopUp : DiseaseEffect
- {
- [DataField("message")]
- public string Message = "disease-sick-generic";
-
- [DataField("type")]
- public PopupRecipients Type = PopupRecipients.Local;
-
- [DataField("visualType")]
- public PopupType VisualType = PopupType.Small;
-
- public override void Effect(DiseaseEffectArgs args)
- {
- var popupSys = args.EntityManager.EntitySysManager.GetEntitySystem<SharedPopupSystem>();
-
- if (Type == PopupRecipients.Local)
- popupSys.PopupEntity(Loc.GetString(Message), args.DiseasedEntity, args.DiseasedEntity, VisualType);
- else if (Type == PopupRecipients.Pvs)
- popupSys.PopupEntity(Loc.GetString(Message, ("person", Identity.Entity(args.DiseasedEntity, args.EntityManager))), args.DiseasedEntity, VisualType);
- }
-
- }
-
- public enum PopupRecipients
- {
- Pvs,
- Local
- }
-}
+++ /dev/null
-using Content.Shared.Chat.Prototypes;
-using Content.Shared.Disease;
-using JetBrains.Annotations;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Disease
-{
- /// <summary>
- /// Makes the diseased sneeze or cough
- /// or neither.
- /// </summary>
- [UsedImplicitly]
- public sealed class DiseaseSnough : DiseaseEffect
- {
- /// <summary>
- /// Emote to play when snoughing
- /// </summary>
- [DataField("emote", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
- public string EmoteId = String.Empty;
-
- /// <summary>
- /// Whether to spread the disease through the air
- /// </summary>
- [DataField("airTransmit")]
- public bool AirTransmit = true;
-
- public override void Effect(DiseaseEffectArgs args)
- {
- EntitySystem.Get<DiseaseSystem>().SneezeCough(args.DiseasedEntity, args.Disease, EmoteId, AirTransmit);
- }
- }
-}
+++ /dev/null
-using JetBrains.Annotations;
-using Content.Shared.Disease;
-using Content.Server.Medical;
-
-namespace Content.Server.Disease.Effects
-{
- /// <summary>
- /// Forces you to vomit.
- /// </summary>
- [UsedImplicitly]
- public sealed class DiseaseVomit : DiseaseEffect
- {
- /// How many units of thirst to add each time we vomit
- [DataField("thirstAmount")]
- public float ThirstAmount = -40f;
- /// How many units of hunger to add each time we vomit
- [DataField("hungerAmount")]
- public float HungerAmount = -40f;
-
- public override void Effect(DiseaseEffectArgs args)
- {
- var vomitSys = args.EntityManager.EntitySysManager.GetEntitySystem<VomitSystem>();
-
- vomitSys.Vomit(args.DiseasedEntity, ThirstAmount, HungerAmount);
- }
- }
-}
+++ /dev/null
-using Content.Shared.Disease;
-
-
-namespace Content.Server.Disease.Components
-{
- [RegisterComponent]
- public sealed class DiseaseServerComponent : Component
- {
- /// <summary>
- /// Which diseases this server has information on.
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- public List<DiseasePrototype> Diseases = new();
- }
-}
public Dictionary<string, string> InitialInfectedNames = new();
public string PatientZeroPrototypeID = "InitialInfected";
- public string InitialZombieVirusPrototype = "PassiveZombieVirus";
public const string ZombifySelfActionPrototype = "TurnUndead";
}
using System.Linq;
using Content.Server.Actions;
using Content.Server.Chat.Managers;
-using Content.Server.Disease;
-using Content.Server.Disease.Components;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Mind.Components;
using Content.Server.Players;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IServerPreferencesManager _prefs = default!;
[Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
- [Dependency] private readonly DiseaseSystem _diseaseSystem = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly ActionsSystem _action = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
var prefList = new List<IPlayerSession>();
foreach (var player in allPlayers)
{
- if (player.AttachedEntity != null && HasComp<DiseaseCarrierComponent>(player.AttachedEntity))
+ // TODO: A
+ if (player.AttachedEntity != null && HasComp<HumanoidAppearanceComponent>(player.AttachedEntity))
{
playerList.Add(player);
var inCharacterName = string.Empty;
if (mind.OwnedEntity != null)
{
- _diseaseSystem.TryAddDisease(mind.OwnedEntity.Value, component.InitialZombieVirusPrototype);
+ EnsureComp<PendingZombieComponent>(mind.OwnedEntity.Value);
+ EnsureComp<ZombifyOnDeathComponent>(mind.OwnedEntity.Value);
inCharacterName = MetaData(mind.OwnedEntity.Value).EntityName;
var action = new InstantAction(_prototypeManager.Index<InstantActionPrototype>(ZombieRuleComponent.ZombifySelfActionPrototype));
using Content.Server.UserInterface;
-using Content.Shared.Disease;
using Content.Shared.MedicalScanner;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Medical.Components
{
/// </summary>
[DataField("scanningEndSound")]
public SoundSpecifier? ScanningEndSound;
-
- /// <summary>
- /// The disease this will give people.
- /// </summary>
- [DataField("disease", customTypeSerializer: typeof(PrototypeIdSerializer<DiseasePrototype>))]
- [ViewVariables(VVAccess.ReadWrite)]
- public string? Disease;
}
}
-using Content.Server.Disease;
using Content.Server.Medical.Components;
-using Content.Server.Popups;
using Content.Server.PowerCell;
-using Content.Server.UserInterface;
using Content.Shared.Damage;
using Content.Shared.DoAfter;
-using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.MedicalScanner;
using Content.Shared.Mobs.Components;
{
public sealed class HealthAnalyzerSystem : EntitySystem
{
- [Dependency] private readonly DiseaseSystem _disease = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly PowerCellSystem _cell = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
_audio.PlayPvs(component.ScanningEndSound, args.Args.User);
UpdateScannedUser(uid, args.Args.User, args.Args.Target.Value, component);
- // Below is for the traitor item
- // Piggybacking off another component's doafter is complete CBT so I gave up
- // and put it on the same component
- /*
- * this code is cursed wuuuuuuut
- */
- if (string.IsNullOrEmpty(component.Disease))
- {
- args.Handled = true;
- return;
- }
-
- _disease.TryAddDisease(args.Args.Target.Value, component.Disease);
-
- if (args.Args.User == args.Args.Target)
- {
- _popupSystem.PopupEntity(Loc.GetString("disease-scanner-gave-self", ("disease", component.Disease)),
- args.Args.User, args.Args.User);
- }
- else
- {
- _popupSystem.PopupEntity(Loc.GetString("disease-scanner-gave-other", ("target", Identity.Entity(args.Args.Target.Value, EntityManager)),
- ("disease", component.Disease)), args.Args.User, args.Args.User);
- }
-
args.Handled = true;
}
using Content.Shared.Throwing;
using Content.Server.Storage.EntitySystems;
using Content.Shared.Interaction;
-using Content.Server.Disease;
-using Content.Server.Disease.Components;
using Content.Shared.Item;
using Content.Shared.Bed.Sleep;
using System.Linq;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Revenant.Components;
-using Content.Shared.Revenant.EntitySystems;
using Robust.Shared.Physics.Components;
using Robust.Shared.Utility;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly EntityStorageSystem _entityStorage = default!;
- [Dependency] private readonly DiseaseSystem _disease = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
return;
args.Handled = true;
-
- var emo = GetEntityQuery<DiseaseCarrierComponent>();
- foreach (var ent in _lookup.GetEntitiesInRange(uid, component.BlightRadius))
- {
- if (emo.TryGetComponent(ent, out var comp))
- _disease.TryAddDisease(ent, component.BlightDiseasePrototypeId, comp);
- }
+ // TODO: When disease refactor is in.
}
private void OnMalfunctionAction(EntityUid uid, RevenantComponent component, RevenantMalfunctionActionEvent args)
+++ /dev/null
-using Content.Server.StationEvents.Events;
-
-namespace Content.Server.StationEvents.Components;
-
-[RegisterComponent, Access(typeof(DiseaseOutbreakRule))]
-public sealed class DiseaseOutbreakRuleComponent : Component
-{
- /// <summary>
- /// Disease prototypes I decided were not too deadly for a random event
- /// </summary>
- /// <remarks>
- /// Fire name
- /// </remarks>
- [DataField("notTooSeriousDiseases")]
- public readonly IReadOnlyList<string> NotTooSeriousDiseases = new[]
- {
- "SpaceCold",
- "VanAusdallsRobovirus",
- "VentCough",
- "AMIV",
- "SpaceFlu",
- "BirdFlew",
- "TongueTwister"
- };
-}
+++ /dev/null
-using Content.Server.Disease;
-using Content.Server.Disease.Components;
-using Content.Server.GameTicking.Rules.Components;
-using Content.Server.StationEvents.Components;
-using Content.Shared.Disease;
-using Content.Shared.Mobs.Components;
-using Content.Shared.Mobs.Systems;
-using Robust.Shared.Random;
-
-namespace Content.Server.StationEvents.Events;
-/// <summary>
-/// Infects a couple people
-/// with a random disease that isn't super deadly
-/// </summary>
-public sealed class DiseaseOutbreakRule : StationEventSystem<DiseaseOutbreakRuleComponent>
-{
- [Dependency] private readonly DiseaseSystem _diseaseSystem = default!;
- [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
-
- /// <summary>
- /// Finds 2-5 random, alive entities that can host diseases
- /// and gives them a randomly selected disease.
- /// They all get the same disease.
- /// </summary>
- protected override void Started(EntityUid uid, DiseaseOutbreakRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
- {
- base.Started(uid, component, gameRule, args);
-
- HashSet<EntityUid> stationsToNotify = new();
- List<DiseaseCarrierComponent> aliveList = new();
- foreach (var (carrier, mobState) in EntityQuery<DiseaseCarrierComponent, MobStateComponent>())
- {
- if (!_mobStateSystem.IsDead(mobState.Owner, mobState))
- aliveList.Add(carrier);
- }
- RobustRandom.Shuffle(aliveList);
-
- // We're going to filter the above out to only alive mobs. Might change after future mobstate rework
- var toInfect = RobustRandom.Next(2, 5);
-
- var diseaseName = RobustRandom.Pick(component.NotTooSeriousDiseases);
-
- if (!PrototypeManager.TryIndex(diseaseName, out DiseasePrototype? disease))
- return;
-
- // Now we give it to people in the list of living disease carriers earlier
- foreach (var target in aliveList)
- {
- if (toInfect-- == 0)
- break;
-
- _diseaseSystem.TryAddDisease(target.Owner, disease, target);
-
- var station = StationSystem.GetOwningStation(target.Owner);
- if(station == null) continue;
- stationsToNotify.Add((EntityUid) station);
- }
- }
-}
+++ /dev/null
-using Content.Shared.Chat.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Traits.Assorted;
-
-/// <summary>
-/// This is used for the occasional sneeze or cough.
-/// </summary>
-[RegisterComponent]
-public sealed class UncontrollableSnoughComponent : Component
-{
- /// <summary>
- /// Emote to play when snoughing
- /// </summary>
- [DataField("emote", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
- public string EmoteId = String.Empty;
-
- /// <summary>
- /// The random time between incidents, (min, max).
- /// </summary>
- [DataField("timeBetweenIncidents", required: true)]
- public Vector2 TimeBetweenIncidents { get; }
-
- public float NextIncidentTime;
-}
+++ /dev/null
-using Content.Server.Disease;
-using Robust.Shared.Random;
-
-namespace Content.Server.Traits.Assorted;
-
-/// <summary>
-/// This handles making people randomly cough/sneeze without a disease.
-/// </summary>
-public sealed class UncontrollableSnoughSystem : EntitySystem
-{
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly DiseaseSystem _diseaseSystem = default!;
-
- /// <inheritdoc/>
- public override void Initialize()
- {
- SubscribeLocalEvent<UncontrollableSnoughComponent, ComponentStartup>(SetupSnough);
- }
-
- private void SetupSnough(EntityUid uid, UncontrollableSnoughComponent component, ComponentStartup args)
- {
- component.NextIncidentTime =
- _random.NextFloat(component.TimeBetweenIncidents.X, component.TimeBetweenIncidents.Y);
- }
-
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
-
- var query = EntityQueryEnumerator<UncontrollableSnoughComponent>();
- while (query.MoveNext(out var ent, out var snough))
- {
- snough.NextIncidentTime -= frameTime;
-
- if (snough.NextIncidentTime >= 0)
- continue;
-
- // Set the new time.
- snough.NextIncidentTime +=
- _random.NextFloat(snough.TimeBetweenIncidents.X, snough.TimeBetweenIncidents.Y);
-
- _diseaseSystem.SneezeCough(ent, null, snough.EmoteId, false);
- }
- }
-}
+++ /dev/null
-using Content.Shared.Disease;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
-
-namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
-/// <summary>
-/// Spawn a random disease at regular intervals when artifact activated.
-/// </summary>
-[RegisterComponent]
-public sealed class DiseaseArtifactComponent : Component
-{
- /// <summary>
- /// The diseases that the artifact can use.
- /// </summary>
- [DataField("diseasePrototype", customTypeSerializer: typeof(PrototypeIdListSerializer<DiseasePrototype>))]
- public List<string> DiseasePrototypes = new();
-
- /// <summary>
- /// Disease the artifact will spawn
- /// Picks a random one from its list
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- public DiseasePrototype? SpawnDisease;
-
- /// <summary>
- /// How far away it will check for people
- /// If empty, picks a random one from its list
- /// </summary>
- [DataField("range"), ViewVariables(VVAccess.ReadWrite)]
- public float Range = 5f;
-}
+++ /dev/null
-using System.Linq;
-using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
-using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
-using Content.Shared.Disease;
-using Content.Server.Disease;
-using Content.Server.Disease.Components;
-using Robust.Shared.Prototypes;
-using Content.Shared.Interaction;
-
-namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems
-{
- /// <summary>
- /// Handles disease-producing artifacts
- /// </summary>
- public sealed class DiseaseArtifactSystem : EntitySystem
- {
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly DiseaseSystem _disease = default!;
- [Dependency] private readonly EntityLookupSystem _lookup = default!;
- [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
-
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<DiseaseArtifactComponent, ArtifactNodeEnteredEvent>(OnNodeEntered);
- SubscribeLocalEvent<DiseaseArtifactComponent, ArtifactActivatedEvent>(OnActivate);
- }
-
- /// <summary>
- /// Makes sure this artifact is assigned a disease
- /// </summary>
- private void OnNodeEntered(EntityUid uid, DiseaseArtifactComponent component, ArtifactNodeEnteredEvent args)
- {
- if (component.SpawnDisease != null || !component.DiseasePrototypes.Any())
- return;
- var diseaseName = component.DiseasePrototypes[args.RandomSeed % component.DiseasePrototypes.Count];
-
- if (!_prototypeManager.TryIndex<DiseasePrototype>(diseaseName, out var disease))
- {
- Logger.ErrorS("disease", $"Invalid disease {diseaseName} selected from random diseases.");
- return;
- }
-
- component.SpawnDisease = disease;
- }
-
- /// <summary>
- /// When activated, blasts everyone in LOS within n tiles
- /// with a high-probability disease infection attempt
- /// </summary>
- private void OnActivate(EntityUid uid, DiseaseArtifactComponent component, ArtifactActivatedEvent args)
- {
- if (component.SpawnDisease == null) return;
-
- var xform = Transform(uid);
- var carrierQuery = GetEntityQuery<DiseaseCarrierComponent>();
-
- foreach (var entity in _lookup.GetEntitiesInRange(xform.Coordinates, component.Range))
- {
- if (!carrierQuery.TryGetComponent(entity, out var carrier)) continue;
-
- if (!_interactionSystem.InRangeUnobstructed(uid, entity, component.Range))
- continue;
-
- _disease.TryInfect(carrier, component.SpawnDisease, forced: true);
- }
- }
- }
-}
-
--- /dev/null
+using Content.Shared.Damage;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Server.Zombies;
+
+/// <summary>
+/// Temporary because diseases suck.
+/// </summary>
+[RegisterComponent]
+public sealed class PendingZombieComponent : Component
+{
+ [DataField("damage")] public DamageSpecifier Damage = new()
+ {
+ DamageDict = new Dictionary<string, FixedPoint2>()
+ {
+ { "Blunt", FixedPoint2.New(1) }
+ }
+ };
+
+ [DataField("nextTick", customTypeSerializer:typeof(TimeOffsetSerializer))]
+ public TimeSpan NextTick;
+}
using Content.Server.Chat;
using Content.Server.Chat.Systems;
using Content.Server.Cloning;
-using Content.Server.Disease;
-using Content.Server.Disease.Components;
using Content.Server.Drone.Components;
using Content.Server.Humanoid;
using Content.Server.Inventory;
using Content.Shared.Chemistry.Components;
using Content.Server.Emoting.Systems;
using Content.Server.Speech.EntitySystems;
-using Content.Shared.Disease.Events;
+using Content.Shared.Damage;
using Content.Shared.Inventory;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
{
public sealed class ZombieSystem : SharedZombieSystem
{
- [Dependency] private readonly DiseaseSystem _disease = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IPrototypeManager _protoManager = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
+ [Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly ZombifyOnDeathSystem _zombify = default!;
[Dependency] private readonly ServerInventorySystem _inv = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly AutoEmoteSystem _autoEmote = default!;
[Dependency] private readonly EmoteOnDamageSystem _emoteOnDamage = default!;
- [Dependency] private readonly IGameTiming _gameTiming = default!;
- [Dependency] private readonly IPrototypeManager _protoManager = default!;
- [Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
public override void Initialize()
SubscribeLocalEvent<ZombieComponent, MeleeHitEvent>(OnMeleeHit);
SubscribeLocalEvent<ZombieComponent, MobStateChangedEvent>(OnMobState);
SubscribeLocalEvent<ZombieComponent, CloningEvent>(OnZombieCloning);
- SubscribeLocalEvent<ZombieComponent, AttemptSneezeCoughEvent>(OnSneeze);
SubscribeLocalEvent<ZombieComponent, TryingToSleepEvent>(OnSleepAttempt);
+
+ SubscribeLocalEvent<PendingZombieComponent, MapInitEvent>(OnPendingMapInit);
+ }
+
+ private void OnPendingMapInit(EntityUid uid, PendingZombieComponent component, MapInitEvent args)
+ {
+ component.NextTick = _timing.CurTime;
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+ var query = EntityQueryEnumerator<PendingZombieComponent>();
+ var curTime = _timing.CurTime;
+
+ while (query.MoveNext(out var uid, out var comp))
+ {
+ if (comp.NextTick < curTime)
+ continue;
+
+ comp.NextTick += TimeSpan.FromSeconds(1);
+ _damageable.TryChangeDamage(uid, comp.Damage, true, false);
+ }
}
private void OnSleepAttempt(EntityUid uid, ZombieComponent component, ref TryingToSleepEvent args)
}
}
- private void OnSneeze(EntityUid uid, ZombieComponent component, ref AttemptSneezeCoughEvent args)
- {
- args.Cancelled = true;
- }
-
private float GetZombieInfectionChance(EntityUid uid, ZombieComponent component)
{
var baseChance = component.MaxZombieInfectionChance;
if (!TryComp<MobStateComponent>(entity, out var mobState) || HasComp<DroneComponent>(entity))
continue;
- if (HasComp<DiseaseCarrierComponent>(entity) && _robustRandom.Prob(GetZombieInfectionChance(entity, component)))
- _disease.TryAddDisease(entity, "ActiveZombieVirus");
+ if (_random.Prob(GetZombieInfectionChance(entity, component)))
+ {
+ EnsureComp<PendingZombieComponent>(entity);
+ EnsureComp<ZombifyOnDeathComponent>(entity);
+ }
if (HasComp<ZombieComponent>(entity))
args.BonusDamage = -args.BaseDamage * zombieComp.OtherZombieDamageCoefficient;
using Content.Server.Chat;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
-using Content.Server.Disease.Components;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Humanoid;
using Content.Server.IdentityManagement;
//we need to basically remove all of these because zombies shouldn't
//get diseases, breath, be thirst, be hungry, or die in space
- RemComp<DiseaseCarrierComponent>(target);
RemComp<RespiratorComponent>(target);
RemComp<BarotraumaComponent>(target);
RemComp<HungerComponent>(target);
+++ /dev/null
-namespace Content.Shared.Atmos.Miasma;
-public abstract class SharedFliesComponent : Component
-{
-
-}
+++ /dev/null
-using JetBrains.Annotations;
-
-namespace Content.Shared.Disease
-{
- [ImplicitDataDefinitionForInheritors]
- [MeansImplicitUse]
- public abstract class DiseaseCure
- {
- /// <summary>
- /// This returns true if the disease should be cured
- /// and false otherwise
- /// </summary>
- public abstract bool Cure(DiseaseEffectArgs args);
- /// <summary>
- /// What stages the cure applies to.
- /// probably should be all, but go wild
- /// </summary>
- [DataField("stages")]
- public readonly int[] Stages = { 0 };
- /// <summary>
- /// This is used by the disease diangoser machine
- /// to generate reports to tell people all of a disease's
- /// special cures using in-game methods.
- /// So it should return a localization string describing
- /// the cure
- /// </summary>
- public abstract string CureText();
- }
-}
+++ /dev/null
-using JetBrains.Annotations;
-
-namespace Content.Shared.Disease
-{
- [ImplicitDataDefinitionForInheritors]
- [MeansImplicitUse]
- public abstract class DiseaseEffect
- {
- /// <summary>
- /// What's the chance, from 0 to 1, that this effect will occur?
- /// </summary>
- [DataField("probability")]
- public float Probability = 1.0f;
- /// <summary>
- /// What stages this effect triggers on
- /// </summary>
- [DataField("stages")]
- public readonly int[] Stages = { 0 };
- /// <summary>
- /// What effect the disease will have.
- /// </summary>
- public abstract void Effect(DiseaseEffectArgs args);
- }
- /// <summary>
- /// What you have to work with in any disease effect/cure.
- /// Includes an entity manager because it is out of scope
- /// otherwise.
- /// </summary>
- public readonly record struct DiseaseEffectArgs(
- EntityUid DiseasedEntity,
- DiseasePrototype Disease,
- IEntityManager EntityManager
- );
-}
+++ /dev/null
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Disease
-{
- /// <summary>
- /// Stores bools for if the machine is on
- /// and if it's currently running.
- /// Used for the visualizer
- /// </summary>
- [Serializable, NetSerializable]
- public enum DiseaseMachineVisuals : byte
- {
- IsOn,
- IsRunning
- }
-}
+++ /dev/null
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
-
-namespace Content.Shared.Disease
-{
- /// <summary>
- /// Diseases encompass everything from viruses to cancers to heart disease.
- /// It's not just a virology thing.
- /// </summary>
- [Prototype("disease")]
- [DataDefinition]
- public sealed class DiseasePrototype : IPrototype, IInheritingPrototype
- {
- [ViewVariables]
- [IdDataField]
- public string ID { get; } = default!;
-
- [DataField("name")]
- public string Name { get; private set; } = string.Empty;
-
- [ParentDataFieldAttribute(typeof(AbstractPrototypeIdArraySerializer<DiseasePrototype>))]
- public string[]? Parents { get; private set; }
-
- [NeverPushInheritance]
- [AbstractDataFieldAttribute]
- public bool Abstract { get; private set; }
-
- /// <summary>
- /// Controls how often a disease ticks.
- /// </summary>
- [ViewVariables]
- public float TickTime = 1f;
-
- /// <summary>
- /// Since disease isn't mapped to metabolism or anything,
- /// it needs something to control its tickrate
- /// </summary>
- public float Accumulator = 0f;
- /// <summary>
- /// Since accumulator is reset with TickTime, this just tracks
- /// the total amount of time a disease has been present.
- /// </summary>
- public float TotalAccumulator = 0f;
- /// <summary>
- /// Stores all the separate stages of the disease plus the time
- /// thresholds for their activation
- /// int: the disease stage (0 for baseline, 1, 2, etc.)
- /// float: the time it takes for the stage to begin.
- /// </summary>
- [DataField("stages", serverOnly: true)]
- public readonly List<float> Stages = new() { 0f };
- /// <summary>
- /// List of effects the disease has that will
- /// run every second (by default anyway)
- /// </summary>
- [DataField("effects", serverOnly: true)]
- public readonly List<DiseaseEffect> Effects = new(0);
- /// <summary>
- /// List of SPECIFIC CURES the disease has that will
- /// be checked every second.
- /// Stuff like spaceacillin operates outside this.
- /// </summary>
- [DataField("cures", serverOnly: true)]
- public readonly List<DiseaseCure> Cures = new(0);
- /// <summary>
- /// This flatly reduces the probabilty disease medicine
- /// has to cure it every tick. Although, since spaceacillin is
- /// used as a reference and it has 0.15 chance, this is
- /// a base 33% reduction in cure chance
- /// </summary>
- [DataField("cureResist", serverOnly: true)]
- public float CureResist = 0.05f;
- /// <summary>
- /// Whether the disease can infect other people.
- /// Since this isn't just a virology thing, this
- /// primary determines what sort of disease it is.
- /// This also affects things like the vaccine machine.
- /// You can't print a cancer vaccine
- /// </summary>
- [DataField("infectious", serverOnly: true)]
- public bool Infectious = true;
- }
-}
+++ /dev/null
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Disease.Components
-{
- /// This is added to anyone with at least 1 disease
- /// and helps cull event subscriptions and entity queries
- /// when they are not relevant.
- [NetworkedComponent]
- [RegisterComponent]
- public sealed class DiseasedComponent : Component
- {}
-}
+++ /dev/null
-namespace Content.Shared.Disease.Events;
-
-/// <summary>
-/// Raised by an entity about to sneeze/cough.
-/// Set Cancelled to true on event handling to suppress the sneeze
-/// </summary>
-[ByRefEvent]
-public record struct AttemptSneezeCoughEvent(EntityUid Uid, string? EmoteId, bool Cancelled = false);
+++ /dev/null
-using Content.Shared.DoAfter;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Disease.Events;
-
-[Serializable, NetSerializable]
-public sealed class VaccineDoAfterEvent : SimpleDoAfterEvent
-{
-}
\ No newline at end of file
using Content.Shared.Bed.Sleep;
-using Content.Shared.Disease.Events;
-using Content.Shared.DragDrop;
using Content.Shared.Emoting;
using Content.Shared.Hands;
using Content.Shared.Interaction.Events;
SubscribeLocalEvent<MobStateComponent, UpdateCanMoveEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, StandAttemptEvent>(CheckAct);
SubscribeLocalEvent<MobStateComponent, TryingToSleepEvent>(OnSleepAttempt);
- SubscribeLocalEvent<MobStateComponent, AttemptSneezeCoughEvent>(OnSneezeAttempt);
}
private void OnStateExitSubscribers(EntityUid target, MobStateComponent component, MobState state)
args.Cancelled = true;
}
- private void OnSneezeAttempt(EntityUid target, MobStateComponent component, ref AttemptSneezeCoughEvent args)
- {
- if (IsDead(target, component))
- args.Cancelled = true;
- }
-
private void OnGettingStripped(EntityUid target, MobStateComponent component, BeforeGettingStrippedEvent args)
{
// Incapacitated or dead targets get stripped two or three times as fast. Makes stripping corpses less tedious.
-using Content.Shared.Disease;
using Content.Shared.FixedPoint;
using Content.Shared.Store;
using Robust.Shared.GameStates;
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("blightRadius")]
public float BlightRadius = 3.5f;
-
- /// <summary>
- /// The disease that is given to the victims of the ability.
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite), DataField("blightDiseasePrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer<DiseasePrototype>))]
- public string BlightDiseasePrototypeId = "SpectralTiredness";
#endregion
#region Malfunction Ability
namespace Content.Shared.Swab;
-[Serializable, NetSerializable]
-public sealed class DiseaseSwabDoAfterEvent : SimpleDoAfterEvent
-{
-}
-
[Serializable, NetSerializable]
public sealed class BotanySwabDoAfterEvent : SimpleDoAfterEvent
{
+++ /dev/null
-diagnoser-cant-use-swab = {CAPITALIZE(THE($machine))} rejects {THE($swab)}.
-diagnoser-insert-swab = You insert {THE($swab)} into {THE($machine)}.
-diagnoser-disease-report = Disease Report: {CAPITALIZE($disease)}
-diagnoser-disease-report-none = Bill of Good Health
-diagnoser-disease-report-none-contents = [color=green]No diseases were found in this sample.[/color]
-diagnoser-disease-report-name = Disease Name: {CAPITALIZE($disease)}
-diagnoser-disease-report-infectious = Infectious: [color=red]Yes[/color]
-diagnoser-disease-report-not-infectious = Infectious: [color=green]No[/color]
-diagnoser-disease-report-cureresist-none = Spaceacillin Resistance: [color=green]None[/color]
-diagnoser-disease-report-cureresist-low = Spaceacillin Resistance: [color=yellow]Low[/color]
-diagnoser-disease-report-cureresist-medium = Spaceacillin Resistance: [color=orange]Medium[/color]
-diagnoser-disease-report-cureresist-high = Spaceacillin Resistance: [color=red]High[/color]
-diagnoser-no-cures = The disease has no specific cures.
-diagnoser-cure-has = The disease has the following cures:
-diagnoser-cure-bedrest = Rest in bed for {$time} seconds, or {$sleep} seconds if sleeping.
-diagnoser-cure-reagent = Consume at least {$units}u of {$reagent}.
-diagnoser-cure-wait = It will go away on its own after {$time} seconds.
-diagnoser-cure-temp = Reach a body temperature below {$max}°K or above {$min}°K.
-diagnoser-cure-temp-min = Reach a body temperature above {$min}°K.
-diagnoser-cure-temp-max = Reach a body temperature below {$max}°K.
+++ /dev/null
-# Noninfectious
-disease-proto-ultragigacancer = ultragigacancer
-disease-proto-spectral-tiredness = spectral tiredness
-disease-proto-lung-cancer = Stage IIIA Lung Cancer
-
-# Infectious
-disease-proto-space-cold = space cold
-disease-proto-vent-cough = vent cough
-disease-proto-space-flu = space flu
-disease-proto-bird-flew = bird flew
-disease-proto-robovirus = Van Ausdall's Robovirus
-disease-proto-amiv = AMIV
-disease-proto-amirmir = Memetic Amirmir
-disease-proto-bleeders = Bleeder's Bite
-disease-proto-plague = plague
-disease-proto-owonavirus = OwOnavirus
-disease-proto-tongue-twister = Tongue Twister
-
-# Zombie
-disease-proto-zombie = Zombie Virus
-disease-proto-zombie-passive = Zombie Virus
-disease-cured = You feel a bit better.
-disease-sick-generic = You feel sick.
-disease-eaten-inside = You feel like you're being eaten from the inside.
-disease-banana-compulsion = You really want to eat some bananas.
-disease-beat-chest-compulsion = {CAPITALIZE(THE($person))} beats {POSS-ADJ($person)} chest.
-disease-vomit = {CAPITALIZE(THE($person))} vomits.
-disease-think = You feel like you can't think straight.
-disease-polymorph = You feel your body twist and change form!
+disease-vomit = {CAPITALIZE(THE($person))} vomits.
\ No newline at end of file
+++ /dev/null
-miasma-smell = Something smells foul!
-miasma-rotting = [color=orange]It's rotting![/color]
-miasma-bloated = [color=orangered]It's bloated![/color]
-miasma-extremely-bloated = [color=red]It's extremely bloated![/color]
+++ /dev/null
-disease-scanner-diseased = DISEASED
-disease-scanner-not-diseased = No diseases
-disease-scanner-gave-other = You gave {THE($target)} {CAPITALIZE($disease)}!
-disease-scanner-gave-self = You gave yourself {CAPITALIZE($disease)}! Congratulations!
+++ /dev/null
-swab-already-used = You already used this swab.
-swab-swabbed = You swab {THE($target)}'s mouth.
-swab-mask-blocked = {CAPITALIZE(THE($target))} needs to take off {THE($mask)}.
-swab-used = It looks like it's been used.
-swab-unused = It's clean and ready to use.
-
-botany-swab-from = You carefully collect pollen from the plant.
-botany-swab-to = You carefully dust pollen on the plant.
+++ /dev/null
-vaxx-already-used = You already used this vaccine.
-vaxx-used = It's spent.
-vaxx-unused = It hasn't been spent.
job-description-chef = Keep the station fed with a variety of food items, butcher dead animals to ensure miasma doesn't leak, and help keep the bar lively.
job-description-chemist = Produce medicinal drugs for the doctors to use, research ethically dubious rare chemicals, and produce weapons of war when enemies of the station arrive.
job-description-clown = Entertain the crew through elaborate slapstick routines or terrible jokes.
-job-description-cmo = Manage the resources and personnel of the medical department to keep the crew alive and disease-free.
+job-description-cmo = Manage the resources and personnel of the medical department to keep the crew alive.
job-description-paramedic = Rescue critically injured patients all over the station, sometimes outside too. Stablize them, give them a ride to medbay, and get back out there!
job-description-detective = Investigate crime scenes using forensic tools, ensure that the guilty party is found, and have a couple smokes.
-job-description-doctor = Diagnose and heal crewmembers through medicinal chemicals, vaccinations, and disease cures, along with cloning the dead.
+job-description-doctor = Diagnose and heal crewmembers through medicinal chemicals, vaccinations, along with cloning the dead.
job-description-engineer = Keep the station's main engine & solars active, optimize the power network, and make emergency repairs using your hardsuit.
job-description-ertengineer = Ensure that the station has power and clean air.
job-description-ertjanitor = Ensure that the station is properly cleaned--for morale.
+++ /dev/null
-station-event-disease-outbreak-announcement = Confirmed outbreak of level 7 biohazard aboard the station. All personnel must contain the outbreak.
trait-pacifist-name = Pacifist
-trait-sneezing-name = Runny nose
-trait-sneezing-desc = You sneeze and cough uncontrollably
-
permanent-blindness-trait-examined = [color=lightblue]{CAPITALIZE(POSS-ADJ($target))} eyes are glassy and unfocused. It doesn't seem like {SUBJECT($target)} can see you.[/color]
trait-lightweight-name = Lightweight Drunk
serverEvent: !type:RevenantOverloadLightsActionEvent
useDelay: 20
-- type: instantAction
- id: RevenantBlight
- icon: Interface/Actions/blight.png
- name: Blight
- description: Costs 50 Essence.
- serverEvent: !type:RevenantBlightActionEvent
- useDelay: 20
+#- type: instantAction
+# id: RevenantBlight
+# icon: Interface/Actions/blight.png
+# name: Blight
+# description: Costs 50 Essence.
+# serverEvent: !type:RevenantBlightActionEvent
+# useDelay: 20
- type: instantAction
id: RevenantMalfunction
name: Malfunction
description: Costs 60 Essence.
serverEvent: !type:RevenantMalfunctionActionEvent
- useDelay: 20
\ No newline at end of file
+ useDelay: 20
name: Defile
description: Defiles the surrounding area, ripping up floors, damaging windows, opening containers, and throwing items. Using it leaves you vulnerable to attacks for a short period of time.
productAction: RevenantDefile
- cost:
+ cost:
StolenEssence: 10
categories:
- RevenantAbilities
name: Overload Lights
description: Overloads all nearby lights, causing lights to pulse and sending out dangerous lightning. Using it leaves you vulnerable to attacks for a long period of time.
productAction: RevenantOverloadLights
- cost:
+ cost:
StolenEssence: 25
categories:
- RevenantAbilities
- !type:ListingLimitedStockCondition
stock: 1
-- type: listing
- id: RevenantBlight
- name: Blight
- description: Infects all nearby organisms with an infectious disease that causes toxic buildup and tiredness. Using it leaves you vulnerable to attacks for a medium period of time.
- productAction: RevenantBlight
- cost:
- StolenEssence: 75
- categories:
- - RevenantAbilities
- conditions:
- - !type:ListingLimitedStockCondition
- stock: 1
+#- type: listing
+# id: RevenantBlight
+# name: Blight
+# description: Infects all nearby organisms with an infectious disease that causes toxic buildup and tiredness. Using it leaves you vulnerable to attacks for a medium period of time.
+# productAction: RevenantBlight
+# cost:
+# StolenEssence: 75
+# categories:
+# - RevenantAbilities
+# conditions:
+# - !type:ListingLimitedStockCondition
+# stock: 1
- type: listing
id: RevenantMalfunction
name: Malfunction
description: Makes nearby electronics stop working properly. Using it leaves you vulnerable to attacks for a long period of time.
productAction: RevenantMalfunction
- cost:
+ cost:
StolenEssence: 125
categories:
- RevenantAbilities
+++ /dev/null
-- type: disease
- id: ActiveHonkVirus
- name: disease-proto-honk
- infectious: false
- cureResist: 0.2
- stages:
- - 0
- - 120
- - 780
- effects:
- # toxin
- - !type:DiseaseAdjustReagent
- probability: 0.07
- reagent: Toxin
- amount: 1
- stages:
- - 0
- - 1
- - 2
- # honks
- - !type:DiseaseHonk
- probability: 0.03
- emote: Honk
- stages:
- - 0
- - !type:DiseaseHonk
- probability: 0.04
- emote: Honk
- stages:
- - 1
- - !type:DiseaseHonk
- probability: 0.06
- emote: Honk
- stages:
- - 2
- # stuttering accent chance when speaking
- - !type:DiseaseGenericStatusEffect
- probability: 0.3
- key: Stutter
- component: StutteringAccent
- stages:
- - 1
- - !type:DiseaseGenericStatusEffect
- probability: 0.6
- key: Stutter
- component: StutteringAccent
- stages:
- - 2
- # possible cluwnification if ignored too long
- - !type:DiseaseAddComponent
- comp: Cluwne
- probability: 0.0007
- stages:
- - 2
- cures:
- - !type:DiseaseReagentCure
- reagent: BananaHonk
- min: 5
- - !type:DiseaseJustWaitCure
- maxLength: 900
\ No newline at end of file
+++ /dev/null
-- type: disease
- id: SpaceCold
- name: disease-proto-space-cold
- cureResist: 0
- effects:
- - !type:DiseaseAdjustReagent
- probability: 0.2
- reagent: Histamine
- amount: 0.5
- - !type:DiseasePopUp
- probability: 0.025
- - !type:DiseaseSnough
- probability: 0.025
- emote: Sneeze
- cures:
- - !type:DiseaseBedrestCure
- maxLength: 20
- - !type:DiseaseJustWaitCure
- maxLength: 400
- - !type:DiseaseReagentCure
- reagent: Ultravasculine
-### - !type:DiseaseReagentCure ### In Loving Memory, Lean
-### reagent: Lean ### 2022/03/12 - 2022/03/13
-
-- type: disease
- id: VentCough
- name: disease-proto-vent-cough
- effects:
- - !type:DiseasePopUp
- probability: 0.025
- message: generic-reagent-effect-burning-insides
- visualType: Medium
- - !type:DiseaseSnough
- probability: 0.025
- emote: Cough
- - !type:DiseaseHealthChange
- probability: 0.015
- damage:
- types:
- Caustic: 1
- cures:
- - !type:DiseaseBedrestCure
- maxLength: 30
- - !type:DiseaseJustWaitCure
- maxLength: 600
- - !type:DiseaseReagentCure
- reagent: SpaceCleaner
-
-- type: disease
- id: SpaceFlu
- name: disease-proto-space-flu
- cureResist: 0.08
- effects:
- - !type:DiseaseVomit
- probability: 0.01
- - !type:DiseasePopUp
- probability: 0.025
- - !type:DiseaseSnough
- probability: 0.025
- emote: Sneeze
- - !type:DiseaseHealthChange
- probability: 0.015
- damage:
- types:
- Heat: 1
- cures:
- - !type:DiseaseBedrestCure
- maxLength: 100
-
-- type: disease
- id: BirdFlew
- name: disease-proto-bird-flew
- cureResist: 0.08
- effects:
- - !type:DiseaseVomit
- probability: 0.015
- - !type:DiseasePopUp
- probability: 0.025
- - !type:DiseaseSnough
- probability: 0.025
- emote: Cough
- - !type:DiseaseHealthChange
- probability: 0.05
- damage:
- types:
- Caustic: 1
- cures:
- - !type:DiseaseBedrestCure
- maxLength: 120
-
-- type: disease
- id: VanAusdallsRobovirus
- name: disease-proto-robovirus
- cureResist: 0.1
- effects:
- - !type:DiseaseAdjustReagent
- probability: 0.025
- reagent: Licoxide
- amount: 0.5
- - !type:DiseaseSnough
- probability: 0.02
- emote: RobotBeep
- cures:
- - !type:DiseaseJustWaitCure
- maxLength: 900
- - !type:DiseaseReagentCure
- reagent: BeepskySmash
-
-- type: disease
- id: AMIV
- name: disease-proto-amiv
- cureResist: 0.10
- stages:
- - 0
- - 120
- - 780
- effects:
- # compulsion pop ups
- - !type:DiseasePopUp
- probability: 0.015
- type: Pvs
- message: disease-beat-chest-compulsion
- visualType: Medium
- stages:
- - 0
- - 1
- - 2
- - !type:DiseasePopUp
- probability: 0.03
- message: disease-banana-compulsion
- visualType: Medium
- stages:
- - 0
- - 1
- - 2
- # Screeches - spreads disease
- - !type:DiseaseSnough
- probability: 0.01
- emote: MonkeyScreeches
- stages:
- - 0
- - !type:DiseaseSnough
- probability: 0.02
- emote: MonkeyScreeches
- stages:
- - 1
- - !type:DiseaseSnough
- probability: 0.04
- emote: MonkeyScreeches
- stages:
- - 2
- # monkey accent chance when speaking
- - !type:DiseaseGenericStatusEffect
- probability: 0.2
- key: Stutter
- component: MonkeyAccent
- stages:
- - 1
- - !type:DiseaseGenericStatusEffect
- probability: 0.5
- key: Stutter
- component: MonkeyAccent
- stages:
- - 2
- # asphyxiation damage, probably from all the screeching
- - !type:DiseaseHealthChange
- probability: 0.53
- damage:
- types:
- Asphyxiation: 1
- stages:
- - 1
- - 2
- # possible monkefication if ignored too long
- - !type:DiseasePolymorph
- probability: 0.000427 ## ~5% chance over 120 secs
- polymorphId: AMIVMorph
- polymorphMessage: disease-polymorph
- polymorphSound:
- path: /Audio/Animals/monkey_scream.ogg
- stages:
- - 2
- cures:
- - !type:DiseaseJustWaitCure
- maxLength: 900
- stages:
- - 0
- - 1
- - 2
- - !type:DiseaseReagentCure
- reagent: BananaHonk
- stages:
- - 0
- - 1
- - 2
-
-- type: disease
- id: MemeticAmirmir
- name: disease-proto-amirmir
- effects:
- - !type:DiseaseGenericStatusEffect
- probability: 0.015
- key: ForcedSleep
- component: ForcedSleeping
- time: 3
- type: Add
- - !type:DiseaseSnough
- probability: 0.025
- emote: Yawn
-
-- type: disease
- id: BleedersBite
- name: disease-proto-bleeders
- effects:
- - !type:DiseaseAdjustReagent
- reagent: TranexamicAcid
- amount: -2.5
- - !type:DiseaseHealthChange
- probability: 0.015
- damage:
- types:
- Piercing: 20
- - !type:DiseasePopUp
- probability: 0.05
- message: disease-eaten-inside
- visualType: Medium
- cures:
- - !type:DiseaseJustWaitCure
- maxLength: 900
- - !type:DiseaseBodyTemperatureCure
- min: 360
- - !type:DiseaseReagentCure
- reagent: DemonsBlood
-
-- type: disease
- id: Plague
- name: disease-proto-plague
- cureResist: 0.1
- effects:
- - !type:DiseaseVomit
- probability: 0.005
- - !type:DiseasePopUp
- probability: 0.025
- - !type:DiseaseSnough
- probability: 0.025
- emote: Cough
- - !type:DiseaseHealthChange
- probability: 0.05
- damage:
- types:
- Poison: 2
- cures:
- - !type:DiseaseBedrestCure
- maxLength: 120
- - !type:DiseaseJustWaitCure
- maxLength: 240
-
-- type: disease
- id: OwOnavirus
- name: disease-proto-owonavirus
- cureResist: 0.25
- effects:
- - !type:DiseaseGenericStatusEffect
- key: Stutter
- component: OwOAccent
- - !type:DiseaseAdjustReagent ## 20 / 0.013 / 60 is around 25 minutes before overdose (0.5u metabolize each tick)
- probability: 0.513
- reagent: Ephedrine
- amount: 1
- - !type:DiseaseSnough
- probability: 0.01
- emote: CatMeow
- - !type:DiseaseSnough
- probability: 0.01
- emote: CatHisses
- cures:
- - !type:DiseaseBodyTemperatureCure
- min: 420 ## Reachable with a flamer
- - !type:DiseaseReagentCure
- reagent: Theobromine
- min: 4
-
-- type: disease
- id: TongueTwister
- name: disease-proto-tongue-twister
- cureResist: 0.1
- effects:
- - !type:DiseaseGenericStatusEffect
- key: Stutter
- component: ScrambledAccent
- - !type:DiseaseSnough
- probability: 0.01
- emote: Sneeze
- - !type:DiseasePopUp
- probability: 0.02
- message: disease-think
- cures:
- - !type:DiseaseBedrestCure
- maxLength: 30
- - !type:DiseaseJustWaitCure
- maxLength: 400
+++ /dev/null
-- type: disease
- id: Ultragigacancer
- name: disease-proto-ultragigacancer
- infectious: false
- cureResist: 0.15
- effects:
- - !type:DiseaseHealthChange
- probability: 0.3
- damage:
- types:
- Cellular: 1
- - !type:DiseaseVomit
- probability: 0.01
- - !type:DiseasePopUp
- probability: 0.03
- cures:
- - !type:DiseaseReagentCure
- reagent: Phalanximine
- min: 15
-
-- type: disease
- id: SpectralTiredness
- name: disease-proto-spectral-tiredness
- infectious: false
- effects:
- - !type:DiseaseGenericStatusEffect
- probability: 0.03
- key: ForcedSleep
- component: ForcedSleeping
- time: 3
- type: Add
- - !type:DiseaseSnough
- probability: 0.025
- emote: Yawn
- - !type:DiseaseHealthChange
- probability: 0.02
- damage:
- types:
- Poison: 4
- cures:
- - !type:DiseaseJustWaitCure
- maxLength: 240
- - !type:DiseaseBedrestCure
- maxLength: 60
-
-- type: disease
- id: StageIIIALungCancer
- name: disease-proto-lung-cancer
- infectious: false
- cureResist: 1.0
- effects:
- - !type:DiseaseHealthChange
- probability: 0.3
- damage:
- types:
- Cellular: 1
- - !type:DiseaseVomit
- probability: 0.01
- - !type:DiseaseSnough
- probability: 0.10
- emote: Cough
- - !type:DiseasePopUp
- probability: 0.03
-
-
-### Once radiation is refactored I want it to have a small chance of giving you regular cancer
+++ /dev/null
-- type: disease
- id: ActiveZombieVirus
- name: disease-proto-zombie
- infectious: false
- cureResist: 0.2
- effects:
- - !type:DiseaseHealthChange
- probability: 0.075
- damage:
- types:
- Blunt: 4
- - !type:DiseaseAdjustReagent
- probability: 0.05
- reagent: Toxin
- amount: 1
- - !type:DiseaseSnough
- probability: 0.01
- emote: Cough
- - !type:DiseaseAddComponent
- comp: ZombifyOnDeath
- cures:
- - !type:DiseaseReagentCure
- reagent: Romerol
- min: 5
-
-- type: disease
- id: PassiveZombieVirus
- name: disease-proto-zombie-passive
- infectious: false
- cureResist: 1 #no cure. Death is your cure.
- effects:
- - !type:DiseaseAddComponent
- comp: ZombifyOnDeath
state: icon
- type: Clothing
slots: [gloves]
- - type: DiseaseProtection
- protection: 0.05
sprite: Clothing/Hands/Gloves/latex.rsi
- type: Clothing
sprite: Clothing/Hands/Gloves/latex.rsi
- - type: DiseaseProtection
- protection: 0.1
- type: Fiber
fiberMaterial: fibers-latex
- type: FingerprintMask
sprite: Clothing/Hands/Gloves/Color/blue.rsi
- type: Clothing
sprite: Clothing/Hands/Gloves/Color/blue.rsi
- - type: DiseaseProtection
- protection: 0.15
- type: Fiber
fiberMaterial: fibers-nitrile
- type: FingerprintMask
tags:
- HidesHair
- WhitelistChameleon
- - type: DiseaseProtection
- protection: 0.05
- type: IdentityBlocker
- type: entity
tags:
- HidesHair
- WhitelistChameleon
- - type: DiseaseProtection
- protection: 0.05
- type: IdentityBlocker
- type: entity
- type: PressureProtection
highPressureMultiplier: 0.6
lowPressureMultiplier: 5500
- - type: DiseaseProtection
- protection: 0.15
- type: entity
parent: ClothingHeadHardsuitWithLightBase
- type: PressureProtection
highPressureMultiplier: 0.6
lowPressureMultiplier: 5500
- - type: DiseaseProtection
- protection: 0.15
- type: entity
parent: ClothingHeadHardsuitWithLightBase
Piercing: 0.8
Heat: 0.2
Radiation: 0.5
- - type: DiseaseProtection
- protection: 0.15
- type: entity
parent: ClothingHeadHardsuitWithLightBase
Shock: 0.1
Cold: 0.2
Radiation: 0.2
- - type: DiseaseProtection
- protection: 0.25
- type: entity
parent: ClothingHeadHardsuitBase
sprite: Clothing/Head/Hardsuits/ERThelmets/ertmedical.rsi
- type: PointLight
color: "#adffec"
- - type: DiseaseProtection
- protection: 0.20
- type: entity
parent: ClothingHeadHelmetHardsuitSyndie
- type: Clothing
sprite: Clothing/Head/Helmets/paramedhelm.rsi
- type: IngestionBlocker
- - type: DiseaseProtection
- protection: 0.15
- type: TemperatureProtection
coefficient: 0.1
- type: Armor
sprite: Clothing/Head/Hoods/Bio/general.rsi
- type: Clothing
sprite: Clothing/Head/Hoods/Bio/general.rsi
- - type: DiseaseProtection
- protection: 0.15
- type: Tag
tags:
- HidesHair
sprite: Clothing/Head/Hoods/Bio/cmo.rsi
- type: Clothing
sprite: Clothing/Head/Hoods/Bio/cmo.rsi
- - type: DiseaseProtection
- protection: 0.25
- type: entity
parent: ClothingHeadHatHoodBioGeneral
sprite: Clothing/Head/Hoods/Bio/virology.rsi
- type: Clothing
sprite: Clothing/Head/Hoods/Bio/virology.rsi
- - type: DiseaseProtection
- protection: 0.25
- type: entity
parent: ClothingHeadBase
sprite: Clothing/Mask/gas.rsi
- type: BreathMask
- type: IngestionBlocker
- - type: DiseaseProtection
- protection: 0.05
- type: IdentityBlocker
- type: entity
sprite: Clothing/Mask/gascaptain.rsi
- type: BreathMask
- type: IngestionBlocker
- - type: DiseaseProtection
- protection: 0.05
- type: entity
parent: ClothingMaskGasAtmos
sprite: Clothing/Mask/gascentcom.rsi
- type: BreathMask
- type: IngestionBlocker
- - type: DiseaseProtection
- protection: 0.05
- type: entity
parent: ClothingMaskGas
sprite: Clothing/Mask/medical.rsi
- type: BreathMask
- type: IngestionBlocker
- - type: DiseaseProtection
- protection: 0.10
- type: Tag
tags:
- PetWearable
sprite: Clothing/Mask/breath.rsi
- type: BreathMask
- type: IngestionBlocker
- - type: DiseaseProtection
- protection: 0.05
- type: Tag
tags:
- PetWearable
- type: Clothing
sprite: Clothing/Mask/sterile.rsi
- type: IngestionBlocker
- - type: DiseaseProtection
- protection: 0.1
- type: entity
parent: ClothingMaskBase
sprite: Clothing/Mask/plaguedoctormask.rsi
- type: BreathMask
- type: IngestionBlocker
- - type: DiseaseProtection
- protection: 0.1
- type: IdentityBlocker
- type: entity
Piercing: 0.95
Heat: 0.90
Radiation: 0.25
- - type: DiseaseProtection
- protection: 0.05
- type: ToggleableClothing
- type: ContainerContainer
containers:
sprintModifier: 0.8
- type: Item
size: 80
- - type: DiseaseProtection
- protection: 0.05
sprite: Clothing/OuterClothing/Bio/general.rsi
- type: Clothing
sprite: Clothing/OuterClothing/Bio/general.rsi
- - type: DiseaseProtection
- protection: 0.2
- type: Armor
modifiers:
coefficients:
sprite: Clothing/OuterClothing/Bio/cmo.rsi
- type: Clothing
sprite: Clothing/OuterClothing/Bio/cmo.rsi
- - type: DiseaseProtection
- protection: 0.5
- type: entity
parent: ClothingOuterBioGeneral
sprite: Clothing/OuterClothing/Bio/scientist.rsi
- type: Clothing
sprite: Clothing/OuterClothing/Bio/scientist.rsi
- - type: DiseaseProtection
- protection: 0.3
- type: entity
parent: ClothingOuterBioGeneral
sprite: Clothing/OuterClothing/Bio/security.rsi
- type: Clothing
sprite: Clothing/OuterClothing/Bio/security.rsi
- - type: DiseaseProtection
- protection: 0.3
- type: entity
parent: ClothingOuterBioCmo
sprite: Clothing/OuterClothing/Bio/virology.rsi
- type: Clothing
sprite: Clothing/OuterClothing/Bio/virology.rsi
- - type: DiseaseProtection
- protection: 0.5
- type: PressureProtection
highPressureMultiplier: 0.3
lowPressureMultiplier: 5500
- - type: DiseaseProtection
- protection: 0.2
- type: ClothingSpeedModifier
walkModifier: 0.9
sprintModifier: 0.95
- type: PressureProtection
highPressureMultiplier: 0.3
lowPressureMultiplier: 5500
- - type: DiseaseProtection
- protection: 0.2
- type: ClothingSpeedModifier
walkModifier: 0.8
sprintModifier: 0.85
- type: ClothingSpeedModifier
walkModifier: 0.9
sprintModifier: 0.9
- - type: DiseaseProtection
- protection: 0.15
- type: TemperatureProtection
coefficient: 0.1
- type: Armor
damageCoefficient: 0.3
- type: ToggleableClothing
clothingPrototype: ClothingHeadHelmetHardsuitSyndieElite
- - type: DiseaseProtection
- protection: 0.2
- type: entity
parent: ClothingOuterHardsuitBase
damageCoefficient: 0.7
- type: ToggleableClothing
clothingPrototype: ClothingHeadHelmetCBURN
- - type: DiseaseProtection
- protection: 0.4
#ERT
- type: entity
sprite: Clothing/OuterClothing/Hardsuits/ERTSuits/ertmedical.rsi
- type: ToggleableClothing
clothingPrototype: ClothingHeadHelmetHardsuitERTMedical
- - type: DiseaseProtection
- protection: 0.25
- type: entity
sprite: Clothing/Uniforms/Jumpskirt/cmo.rsi
- type: Clothing
sprite: Clothing/Uniforms/Jumpskirt/cmo.rsi
- - type: DiseaseProtection
- protection: 0.15
- type: entity
parent: ClothingUniformSkirtBase
sprite: Clothing/Uniforms/Jumpskirt/medical.rsi
- type: Clothing
sprite: Clothing/Uniforms/Jumpskirt/medical.rsi
- - type: DiseaseProtection
- protection: 0.1
- type: entity
parent: ClothingUniformSkirtBase
sprite: Clothing/Uniforms/Jumpskirt/paramedic.rsi
- type: Clothing
sprite: Clothing/Uniforms/Jumpskirt/paramedic.rsi
- - type: DiseaseProtection
- protection: 0.1
- type: entity
parent: ClothingUniformBase
sprite: Clothing/Uniforms/Jumpskirt/brigmedic.rsi
- type: Clothing
sprite: Clothing/Uniforms/Jumpskirt/brigmedic.rsi
- - type: DiseaseProtection
- protection: 0.1
- type: entity
parent: ClothingUniformSkirtBase
sprite: Clothing/Uniforms/Jumpsuit/cmo.rsi
- type: Clothing
sprite: Clothing/Uniforms/Jumpsuit/cmo.rsi
- - type: DiseaseProtection
- protection: 0.15
- type: entity
parent: ClothingUniformBase
sprite: Clothing/Uniforms/Jumpsuit/medical.rsi
- type: Clothing
sprite: Clothing/Uniforms/Jumpsuit/medical.rsi
- - type: DiseaseProtection
- protection: 0.1
- type: entity
parent: ClothingUniformBase
sprite: Clothing/Uniforms/Jumpsuit/paramedic.rsi
- type: Clothing
sprite: Clothing/Uniforms/Jumpsuit/paramedic.rsi
- - type: DiseaseProtection
- protection: 0.1
- type: entity
parent: ClothingUniformBase
sprite: Clothing/Uniforms/Jumpsuit/brigmedic.rsi
- type: Clothing
sprite: Clothing/Uniforms/Jumpsuit/brigmedic.rsi
- - type: DiseaseProtection
- protection: 0.1
- type: entity
parent: ClothingUniformBase
sprite: Clothing/Uniforms/Jumpsuit/ert_janitor.rsi
- type: Clothing
sprite: Clothing/Uniforms/Jumpsuit/ert_janitor.rsi
- - type: DiseaseProtection
- protection: 0.1
- type: entity
parent: ClothingUniformBase
sprite: Clothing/Uniforms/Jumpsuit/ert_medic.rsi
- type: Clothing
sprite: Clothing/Uniforms/Jumpsuit/ert_medic.rsi
- - type: DiseaseProtection
- protection: 0.15
- type: entity
parent: ClothingUniformBase
name: action-name-combat
- type: Bloodstream
bloodMaxVolume: 50
- - type: DiseaseCarrier #The other class lab animal and disease vector
- naturalImmunities:
- - AMIV
- - ActiveHonkVirus
- type: CanEscapeInventory
- type: MobPrice
price: 50
Base: splat-1
- type: Bloodstream
bloodMaxVolume: 50
- - type: DiseaseCarrier #Why doesn't this save if it's only on the parent wtf
-
- type: entity
parent: MobMouse
Base: splat-2
- type: Bloodstream
bloodMaxVolume: 50
- - type: DiseaseCarrier
-
- type: entity
name: lizard #Weh
spawned:
- id: ClothingHeadHatFancyCrown #how did that get there?
amount: 1
- - type: DiseaseCarrier
- naturalImmunities:
- - ActiveHonkVirus
- carrierDiseases:
- - VentCough
- - AMIV
- - SpaceCold
- - SpaceFlu
- - BirdFlew
- - VanAusdallsRobovirus
- - BleedersBite
- - Plague
- - TongueTwister
- type: MobPrice
price: 2500 # rat wealth
- type: RandomMetadata
spawned:
- id: FoodMeatRat
amount: 1
- - type: DiseaseCarrier
- naturalImmunities:
- - ActiveHonkVirus
- carrierDiseases:
- - VentCough
- - AMIV
- - SpaceCold
- - SpaceFlu
- - BirdFlew
- - VanAusdallsRobovirus
- - BleedersBite
- - Plague
- - TongueTwister
- type: Vocal
sounds:
Male: Mouse
description: They mostly come at night. Mostly.
components:
- type: Insulated
- - type: DiseaseProtection
- protection: 1
- type: CombatMode
- type: InputMover
- type: MobMover
- ForcedSleep
- TemporaryBlindness
- Pacified
- - type: DiseaseCarrier
- type: Blindable
# Other
- type: Inventory
Male: UnisexReptilian
Female: UnisexReptilian
Unsexed: UnisexReptilian
- - type: DiseaseCarrier
- diseaseResist: 0.1
- type: Damageable
damageContainer: Biological
damageModifierSet: Scale
types:
Asphyxiation: -1.0
maxSaturation: 15
- - type: DiseaseCarrier
- naturalImmunities:
- - Ultragigacancer
- - BleedersBite
- type: entity
save: false
- type: Tag
tags:
- Recyclable
- - type: DiseaseSwab
- type: BotanySwab
- type: entity
sprite: Objects/Specific/Medical/medipen.rsi
netsync: false
state: salpen
- - type: DiseaseVaccine
slots:
cell_slot:
name: power-cell-slot-component-slot-name-default
-
-- type: entity
- parent: HandheldHealthAnalyzer
- id: HandheldHealthAnalyzerGigacancer
- suffix: gigacancer
- components:
- - type: HealthAnalyzer
- fake: true
- disease: Ultragigacancer
-
-## I know admins will want this
-- type: entity
- parent: HandheldHealthAnalyzer
- id: HandheldHealthAnalyzerOwOnavirus
- name: OwOnavirus analyzer
- suffix: admin abuse
- components:
- - type: HealthAnalyzer
- fake: true
- disease: OwOnavirus
-
-- type: entity
- parent: HandheldHealthAnalyzer
- id: HandheldHealthAnalyzerZombie
- name: Zombie Infector
- suffix: Active
- components:
- - type: HealthAnalyzer
- fake: true
- disease: ActiveZombieVirus
-
-- type: entity
- parent: HandheldHealthAnalyzer
- id: HandheldHealthAnalyzerHonk
- suffix: Honk Infector
- components:
- - type: HealthAnalyzer
- fake: true
- disease: ActiveHonkVirus
shader: unshaded
map: ["enum.DiseaseMachineVisualLayers.IsOn"]
netsync: false
- - type: DiseaseDiagnoser
- - type: DiseaseMachine
- machineOutput: DiagnosisReportPaper
- type: Appearance
- - type: DiseaseMachineVisuals
- idleState: icon
- runningState: running
- type: Machine
board: DiagnoserMachineCircuitboard
shader: unshaded
map: ["enum.DiseaseMachineVisualLayers.IsOn"]
netsync: false
- - type: DiseaseVaccineCreator
- - type: DiseaseMachine
- machineOutput: Vaccine
- type: Appearance
- type: DiseaseMachineVisuals
idleState: icon
SheetSteel1:
min: 1
max: 2
- - type: DiseaseServer
- type: AmbientSound
volume: -9
range: 5
duration: 1
- type: BureaucraticErrorRule
-- type: entity
- id: DiseaseOutbreak
- parent: BaseGameRule
- noSpawn: true
- components:
- - type: StationEvent
- startAnnouncement: station-event-disease-outbreak-announcement
- startAudio:
- path: /Audio/Announcements/outbreak7.ogg
- params:
- volume: -4
- weight: 5
- duration: 1
- earliestStart: 15
- - type: DiseaseOutbreakRule
-
- type: entity
id: Dragon
parent: BaseGameRule
metabolisms:
Gas:
effects:
- - !type:ChemMiasmaPoolSource
- conditions:
- - !type:OrganType
- type: Rat
- shouldHave: false
- - !type:ReagentThreshold
- reagent: Miasma
- min: 1
- !type:HealthChange
conditions:
- !type:OrganType
metabolisms:
Medicine:
effects:
- - !type:ChemCureDisease
- !type:HealthChange
damage:
types:
messages: ["ethyloxyephedrine-effect-feeling-awake", "ethyloxyephedrine-effect-clear-mind"]
type: Local
probability: 0.1
-
+
- type: reagent
id: Sigynate
name: reagent-name-sigynate
damage:
types:
Poison: 1
- - !type:ChemCauseRandomDisease
- conditions:
- - !type:ReagentThreshold
- reagent: Mold
- min: 1
- diseases:
- - VentCough
- - SpaceCold
- - SpaceFlu
- - BirdFlew
- - Plague
- - TongueTwister
- - MemeticAmirmir
- type: reagent
id: PolytrinicAcid
damage:
types:
Poison: 2
- - !type:ChemCauseDisease ##Since this mostly just comes from the event you won't ingest that much
- causeChance: 0.6
- disease: VentCough
- type: reagent
id: Corpium
damage:
types:
Cellular: 1
- - !type:ChemCauseDisease
- causeChance: 1
- disease: ActiveZombieVirus
- type: reagent
id: UncookedAnimalProteins
damage:
types:
Poison: 0.06
- - !type:ChemCauseDisease
- causeChance: 1
- disease: ActiveHonkVirus ##makes target honk and potential chance to be cluwned.
- type: trait
- id: UncontrollableSneezing
- name: trait-sneezing-name
- description: trait-sneezing-desc
- whitelist:
- components:
- - DiseaseCarrier
- components:
- - type: UncontrollableSnough
- emote: Sneeze
- timeBetweenIncidents: 0.3, 300
-
-- type: trait
id: LightweightDrunk
name: trait-lightweight-name
description: trait-lightweight-desc
prob: 0.5
maxAmount: 3
-- type: artifactEffect
- id: EffectDisease
- targetDepth: 3
- effectHint: artifact-effect-hint-biochemical
- components:
- - type: DiseaseArtifact
- diseasePrototypes:
- - VanAusdallsRobovirus
- - OwOnavirus
- - BleedersBite
- - Ultragigacancer
- - MemeticAmirmir
- - TongueTwister
- - AMIV
-
- type: artifactEffect
id: EffectRareMaterialSpawn
targetDepth: 3