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;
SubscribeLocalEvent<IncurableZombieComponent, MapInitEvent>(OnPendingMapInit);
SubscribeLocalEvent<ZombifyOnDeathComponent, MobStateChangedEvent>(OnDamageChanged);
-
}
private void OnBeforeRemoveAnomalyOnDeath(Entity<PendingZombieComponent> ent, ref BeforeRemoveAnomalyOnDeathEvent args)
}
}
- 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)
using Content.Shared.Temperature;
using Content.Shared.Verbs;
using Content.Shared.Weapons.Ranged.Events;
+using Content.Shared.Zombies;
namespace Content.Shared.Inventory;
SubscribeLocalEvent<InventoryComponent, SelfBeforeGunShotEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, SelfBeforeClimbEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, CoefficientQueryEvent>(RelayInventoryEvent);
+ SubscribeLocalEvent<InventoryComponent, ZombificationResistanceQueryEvent>(RelayInventoryEvent);
// by-ref events
SubscribeLocalEvent<InventoryComponent, BeforeStaminaDamageEvent>(RefRelayInventoryEvent);
-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;
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)
-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;
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;
--- /dev/null
+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;
+ }
+}
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].
- 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:
Slash: 0.9
Piercing: 0.9
Heat: 0.9
+ - type: ZombificationResistance
+ zombificationResistanceCoefficient: 0.75
#Deathsquad Hardsuit
- type: entity
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
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
- 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
Shock: 0.1
Radiation: 0.1
Caustic: 0.1
+ - type: ZombificationResistance
+ zombificationResistanceCoefficient: 0.25
- type: ClothingSpeedModifier
walkModifier: 1.0
sprintModifier: 1.0
Slash: 0.95
Heat: 0.90
priceMultiplier: 0
+ - type: ZombificationResistance
+ zombificationResistanceCoefficient: 0.55
- type: Food
requiresSpecialDigestion: true
- type: SolutionContainerManager