]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Zombification resistance rework (#36485)
authorUpAndLeaves <92269094+Alpha-Two@users.noreply.github.com>
Wed, 16 Apr 2025 18:21:45 +0000 (19:21 +0100)
committerGitHub <noreply@github.com>
Wed, 16 Apr 2025 18:21:45 +0000 (14:21 -0400)
* initial commit

* Commit cuz beck said so :thumbsup:

* Implement balance changes, and revert some stuff

* fix yaml test real

* Added full stop, ensured display of infection chance, buffed biosuit speed

* Maint reviews, I commit

* Review completed, winter coats nerfed, CMO bio suit removed for future PR

* Final-final commit-REAL (2)-COPY

12 files changed:
Content.Server/Zombies/ZombieSystem.cs
Content.Shared/Inventory/InventorySystem.Relay.cs
Content.Shared/Zombies/SharedZombieSystem.cs
Content.Shared/Zombies/ZombieComponent.cs
Content.Shared/Zombies/ZombificationResistanceComponent.cs [new file with mode: 0644]
Resources/Locale/en-US/zombies/zombie.ftl
Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml
Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml
Resources/Prototypes/Entities/Clothing/Head/hoods.yml
Resources/Prototypes/Entities/Clothing/OuterClothing/bio.yml
Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml
Resources/Prototypes/Entities/Clothing/OuterClothing/wintercoats.yml

index aa5c2682bc7ef6bc6799e7bf2c24e32271f00d72..e34ecb4ad5db1bf85a54909a8853c65e3a4c26d9 100644 (file)
@@ -7,6 +7,7 @@ using Content.Server.Emoting.Systems;
 using Content.Server.Speech.EntitySystems;
 using Content.Server.Roles;
 using Content.Shared.Anomaly.Components;
+using Content.Shared.Armor;
 using Content.Shared.Bed.Sleep;
 using Content.Shared.Cloning.Events;
 using Content.Shared.Damage;
@@ -74,7 +75,6 @@ namespace Content.Server.Zombies
             SubscribeLocalEvent<IncurableZombieComponent, MapInitEvent>(OnPendingMapInit);
 
             SubscribeLocalEvent<ZombifyOnDeathComponent, MobStateChangedEvent>(OnDamageChanged);
-
         }
 
         private void OnBeforeRemoveAnomalyOnDeath(Entity<PendingZombieComponent> ent, ref BeforeRemoveAnomalyOnDeathEvent args)
@@ -199,33 +199,29 @@ namespace Content.Server.Zombies
             }
         }
 
-        private float GetZombieInfectionChance(EntityUid uid, ZombieComponent component)
+        private float GetZombieInfectionChance(EntityUid uid, ZombieComponent zombieComponent)
         {
-            var max = component.MaxZombieInfectionChance;
-
-            if (!_inventory.TryGetContainerSlotEnumerator(uid, out var enumerator, ProtectiveSlots))
-                return max;
+            var chance = zombieComponent.BaseZombieInfectionChance;
 
-            var items = 0f;
-            var total = 0f;
-            while (enumerator.MoveNext(out var con))
+            var armorEv = new CoefficientQueryEvent(ProtectiveSlots);
+            RaiseLocalEvent(uid, armorEv);
+            foreach (var resistanceEffectiveness in zombieComponent.ResistanceEffectiveness.DamageDict)
             {
-                total++;
-                if (con.ContainedEntity != null)
-                    items++;
+                if (armorEv.DamageModifiers.Coefficients.TryGetValue(resistanceEffectiveness.Key, out var coefficient))
+                {
+                    // Scale the coefficient by the resistance effectiveness, very descriptive I know
+                    // For example. With 30% slash resist (0.7 coeff), but only a 60% resistance effectiveness for slash,
+                    // you'll end up with 1 - (0.3 * 0.6) = 0.82 coefficient, or a 18% resistance
+                    var adjustedCoefficient = 1 - ((1 - coefficient) * resistanceEffectiveness.Value.Float());
+                    chance *= adjustedCoefficient;
+                }
             }
 
-            if (total == 0)
-                return max;
-
-            // Everyone knows that when it comes to zombies, socks & sandals provide just as much protection as an
-            // armored vest. Maybe these should be weighted per-item. I.e. some kind of coverage/protection component.
-            // Or at the very least different weights per slot.
+            var zombificationResistanceEv = new ZombificationResistanceQueryEvent(ProtectiveSlots);
+            RaiseLocalEvent(uid, zombificationResistanceEv);
+            chance *= zombificationResistanceEv.TotalCoefficient;
 
-            var min = component.MinZombieInfectionChance;
-            //gets a value between the max and min based on how many items the entity is wearing
-            var chance = (max - min) * ((total - items) / total) + min;
-            return chance;
+            return MathF.Max(chance, zombieComponent.MinZombieInfectionChance);
         }
 
         private void OnMeleeHit(EntityUid uid, ZombieComponent component, MeleeHitEvent args)
index efa88fb23a706d642bee77f4d0f6209283a0d7f3..01d3e8724606b46ca293c51bfbe213278880cb6a 100644 (file)
@@ -22,6 +22,7 @@ using Content.Shared.Strip.Components;
 using Content.Shared.Temperature;
 using Content.Shared.Verbs;
 using Content.Shared.Weapons.Ranged.Events;
+using Content.Shared.Zombies;
 
 namespace Content.Shared.Inventory;
 
@@ -44,6 +45,7 @@ public partial class InventorySystem
         SubscribeLocalEvent<InventoryComponent, SelfBeforeGunShotEvent>(RelayInventoryEvent);
         SubscribeLocalEvent<InventoryComponent, SelfBeforeClimbEvent>(RelayInventoryEvent);
         SubscribeLocalEvent<InventoryComponent, CoefficientQueryEvent>(RelayInventoryEvent);
+        SubscribeLocalEvent<InventoryComponent, ZombificationResistanceQueryEvent>(RelayInventoryEvent);
 
         // by-ref events
         SubscribeLocalEvent<InventoryComponent, BeforeStaminaDamageEvent>(RefRelayInventoryEvent);
index 0388450a8c41cc601569bcadee69c52664d0b28c..076917c4acea30548a59592ec2eed13fa3bc3d55 100644 (file)
@@ -1,4 +1,6 @@
-using Content.Shared.Movement.Systems;
+using Content.Shared.Armor;
+using Content.Shared.Inventory;
+using Content.Shared.Movement.Systems;
 using Content.Shared.NameModifier.EntitySystems;
 
 namespace Content.Shared.Zombies;
@@ -12,6 +14,24 @@ public abstract class SharedZombieSystem : EntitySystem
 
         SubscribeLocalEvent<ZombieComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshSpeed);
         SubscribeLocalEvent<ZombieComponent, RefreshNameModifiersEvent>(OnRefreshNameModifiers);
+        SubscribeLocalEvent<ZombificationResistanceComponent, ArmorExamineEvent>(OnArmorExamine);
+        SubscribeLocalEvent<ZombificationResistanceComponent, InventoryRelayedEvent<ZombificationResistanceQueryEvent>>(OnResistanceQuery);
+    }
+
+    private void OnResistanceQuery(Entity<ZombificationResistanceComponent> ent, ref InventoryRelayedEvent<ZombificationResistanceQueryEvent> query)
+    {
+        query.Args.TotalCoefficient *= ent.Comp.ZombificationResistanceCoefficient;
+    }
+
+    private void OnArmorExamine(Entity<ZombificationResistanceComponent> ent, ref ArmorExamineEvent args)
+    {
+        var value = MathF.Round((1f - ent.Comp.ZombificationResistanceCoefficient) * 100, 1);
+
+        if (value == 0)
+            return;
+
+        args.Msg.PushNewline();
+        args.Msg.AddMarkupOrThrow(Loc.GetString(ent.Comp.Examine, ("value", value)));
     }
 
     private void OnRefreshSpeed(EntityUid uid, ZombieComponent component, RefreshMovementSpeedModifiersEvent args)
index f9576dd3aa15da1e481bcc5fbfa1455ba824e8ae..47ae35b4c5afc3f16165b912100041ccb1cffe2e 100644 (file)
@@ -1,7 +1,7 @@
-using Content.Shared.Antag;
 using Content.Shared.Chat.Prototypes;
 using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Damage;
+using Content.Shared.FixedPoint;
 using Content.Shared.Humanoid;
 using Content.Shared.Roles;
 using Content.Shared.StatusIcon;
@@ -17,17 +17,30 @@ namespace Content.Shared.Zombies;
 public sealed partial class ZombieComponent : Component
 {
     /// <summary>
-    /// The baseline infection chance you have if you are completely nude
+    /// The baseline infection chance you have if you have no protective gear
     /// </summary>
     [ViewVariables(VVAccess.ReadWrite)]
-    public float MaxZombieInfectionChance = 0.80f;
+    public float BaseZombieInfectionChance = 0.75f;
 
     /// <summary>
     /// The minimum infection chance possible. This is simply to prevent
-    /// being invincible by bundling up.
+    /// being overly protected by bundling up.
     /// </summary>
     [ViewVariables(VVAccess.ReadWrite)]
-    public float MinZombieInfectionChance = 0.25f;
+    public float MinZombieInfectionChance = 0.05f;
+
+    /// <summary>
+    /// How effective each resistance type on a piece of armor is. Using a damage specifier for this seems illegal.
+    /// </summary>
+    public DamageSpecifier ResistanceEffectiveness = new()
+    {
+        DamageDict = new ()
+        {
+            {"Slash", 0.5},
+            {"Piercing", 0.3},
+            {"Blunt", 0.1},
+        }
+    };
 
     [ViewVariables(VVAccess.ReadWrite)]
     public float ZombieMovementSpeedDebuff = 0.70f;
diff --git a/Content.Shared/Zombies/ZombificationResistanceComponent.cs b/Content.Shared/Zombies/ZombificationResistanceComponent.cs
new file mode 100644 (file)
index 0000000..58dd997
--- /dev/null
@@ -0,0 +1,46 @@
+using Content.Shared.Inventory;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Zombies;
+
+/// <summary>
+/// An armor-esque component for clothing that grants "resistance" (lowers the chance) against getting infected.
+/// It works on a coefficient system, so 0.3 is better than 0.9, 1 is no resistance, and 0 is full resistance.
+/// </summary>
+[NetworkedComponent, RegisterComponent]
+public sealed partial class ZombificationResistanceComponent : Component
+{
+    /// <summary>
+    ///  The multiplier that will by applied to the zombification chance.
+    /// </summary>
+    [DataField]
+    public float ZombificationResistanceCoefficient = 1;
+
+    /// <summary>
+    /// Examine string for the zombification resistance.
+    /// Passed <c>value</c> from 0 to 100.
+    /// </summary>
+    [DataField]
+    public LocId Examine = "zombification-resistance-coefficient-value";
+}
+
+/// <summary>
+/// Gets the total resistance from the ZombificationResistanceComponent, i.e. just all of them multiplied together.
+/// </summary>
+public sealed class ZombificationResistanceQueryEvent : EntityEventArgs, IInventoryRelayEvent
+{
+    /// <summary>
+    /// All slots to relay to
+    /// </summary>
+    public SlotFlags TargetSlots { get; }
+
+    /// <summary>
+    /// The Total of all Coefficients.
+    /// </summary>
+    public float TotalCoefficient = 1.0f;
+
+    public ZombificationResistanceQueryEvent(SlotFlags slots)
+    {
+        TargetSlots = slots;
+    }
+}
index b46e2ebc30c53e59bfcddaef9186c9c6075c958d..4643cd228ba1ecc51d0185b62541161944368ee8 100644 (file)
@@ -7,3 +7,5 @@ zombie-role-desc =  A malevolent creature of the dead.
 zombie-role-rules = You are a [color={role-type-team-antagonist-color}][bold]{role-type-team-antagonist-name}[/bold][/color]. Search out the living and bite them in order to infect them and turn them into zombies. Work together with the other zombies and remaining initial infected to overtake the station.
 
 zombie-permadeath = This time, you're dead for real.
+
+zombification-resistance-coefficient-value = - [color=violet]Infection[/color] chance reduced by [color=lightblue]{$value}%[/color].
index cc4c84840af3cf83465f1ed0e2cae363219004a2..646a5550370f5f52285b6d736c2200a526c3daf3 100644 (file)
   - type: TemperatureProtection
     heatingCoefficient: 1.05
     coolingCoefficient: 0.7
+  - type: ZombificationResistance
+    zombificationResistanceCoefficient: 0.90
+  - type: Armor # so zombification resistance shows up
+    modifiers:
+      coefficients: { }
   - type: GroupExamine
   - type: HideLayerClothing
     slots:
index 9aa80e9a6a5d6fc4f24f3c3819473b41a4b48c9b..eafd3848647a791f1836369868dd2577ea15686d 100644 (file)
         Slash: 0.9
         Piercing: 0.9
         Heat: 0.9
+  - type: ZombificationResistance
+    zombificationResistanceCoefficient: 0.75
 
 #Deathsquad Hardsuit
 - type: entity
index 1330d38f40465abc247745f76decc755611cf711..0f081679ddf4f07adbffbabf451499372465afaa 100644 (file)
     sprite: Clothing/Head/Hoods/Bio/general.rsi
   - type: BreathMask
   - type: IngestionBlocker
+  - type: GroupExamine
+  - type: Armor
+    modifiers: { }
+  - type: ZombificationResistance
+    zombificationResistanceCoefficient: 0.9
   - type: Tag
     tags:
     - WhitelistChameleon
@@ -33,6 +38,8 @@
     sprite: Clothing/Head/Hoods/Bio/cmo.rsi
   - type: Clothing
     sprite: Clothing/Head/Hoods/Bio/cmo.rsi
+  - type: ZombificationResistance
+    zombificationResistanceCoefficient: 0.8
 
 - type: entity
   parent: ClothingHeadHatHoodBioGeneral
   id: ClothingHeadHatHoodBioVirology
   name: bio hood
   suffix: Virology
-  description: A hood that protects the head and face from biological contaminants.
+  description: A hood that strongly protects the head and face from biological contaminants.
   components:
   - type: IdentityBlocker
   - type: Sprite
     sprite: Clothing/Head/Hoods/Bio/virology.rsi
   - type: Clothing
     sprite: Clothing/Head/Hoods/Bio/virology.rsi
+  - type: ZombificationResistance
+    zombificationResistanceCoefficient: 0.8
 
 - type: entity
   parent: ClothingHeadBase
index fec4d4df6c7499754b1061ceb134e68a012f61bc..fa550a83d6244aa3d09af45d8342c8d4749255b2 100644 (file)
   - type: Armor
     modifiers:
       coefficients:
-        Caustic: 0.5
+        Caustic: 0.75
+  - type: ZombificationResistance
+    zombificationResistanceCoefficient: 0.35
+  - type: GroupExamine
+  - type: ClothingSpeedModifier
+    walkModifier: 1
+    sprintModifier: 0.95
 
 - type: entity
   parent: ClothingOuterBioGeneral
   id: ClothingOuterBioJanitor
   name: bio suit
   suffix: Janitor
-  description: A suit that protects against biological contamination, in Janitor colors.
+  description: A suit that protects against biological contamination and caustic spills.
   components:
   - type: Sprite
     sprite: Clothing/OuterClothing/Bio/janitor.rsi
   - type: Clothing
     sprite: Clothing/OuterClothing/Bio/janitor.rsi
+  - type: Armor
+    modifiers:
+      coefficients:
+        Caustic: 0.4
 
 - type: entity
   parent: ClothingOuterBioGeneral
     sprite: Clothing/OuterClothing/Bio/scientist.rsi
 
 - type: entity
-  parent: ClothingOuterBioGeneral
+  parent: [ClothingOuterBioGeneral, BaseSecurityContraband]
   id: ClothingOuterBioSecurity
   name: bio suit
   suffix: Security
-  description: A suit that protects against biological contamination, in Security colors.
+  description: A suit that protects against biological contamination, kitted out with additional armor.
   components:
   - type: Sprite
     sprite: Clothing/OuterClothing/Bio/security.rsi
   - type: Clothing
     sprite: Clothing/OuterClothing/Bio/security.rsi
+  - type: Armor
+    modifiers:
+      coefficients:
+        Caustic: 0.8
+        Slash: 0.6
+        Piercing: 0.8
+  - type: ZombificationResistance
+    zombificationResistanceCoefficient: 0.4
 
 - type: entity
   parent: ClothingOuterBioGeneral
   id: ClothingOuterBioVirology
   name: bio suit
   suffix: Virology
-  description: A suit that protects against biological contamination, in Virology colors.
+  description: A suit that strongly protects against biological contamination.
   components:
   - type: Sprite
     sprite: Clothing/OuterClothing/Bio/virology.rsi
   - type: Clothing
     sprite: Clothing/OuterClothing/Bio/virology.rsi
+  - type: ZombificationResistance
+    zombificationResistanceCoefficient: 0.25
index a841b5878fcad7cb84d3152fbd05b9c29ecca17c..ba57ef9c538a76fd7a6f56bba08d69da71a02117 100644 (file)
         Shock: 0.1
         Radiation: 0.1
         Caustic: 0.1
+  - type: ZombificationResistance
+    zombificationResistanceCoefficient: 0.25
   - type: ClothingSpeedModifier
     walkModifier: 1.0
     sprintModifier: 1.0
index c1419717006e0fc45e3483140e1ede5113db1788..142cac767e3d8bd487604771b99c4087af1cbda1 100644 (file)
@@ -19,6 +19,8 @@
         Slash: 0.95
         Heat: 0.90
     priceMultiplier: 0
+  - type: ZombificationResistance
+    zombificationResistanceCoefficient: 0.55
   - type: Food
     requiresSpecialDigestion: true
   - type: SolutionContainerManager